Skip to main content

embedding/
wasi.rs

1//! This example demonstrates how wasmtime-wasi-io can be used in a #![no_std]
2//! target as the basis for a WASI implementation.
3//!
4//! This example can execute a wasi:cli/command component on a custom async
5//! executor with no dependencies on the environment: execution is
6//! deterministic, and no sources of input are provided to the component. The
7//! WASI implementation is deliberately limited and incomplete, and many WASI
8//! components will not even instantiate, or execute correctly, because this
9//! is not a fully fleshed-out example.
10//!
11//! The wasmtime-wasi implementation of WASI depends on the tokio executor,
12//! cap-std family of crates, and others to provide a complete implementation
13//! of WASI p2 on top of Unix-based and Windows operating systems. It would be
14//! difficult and/or inappropriate to port to other settings. This example
15//! might be a good starting point for how to go about rolling your own WASI
16//! implementation that is particular to your own execution environment.
17//!
18//! The wasmtime-wasi-io crate, which is a key part of this example, provides
19//! an implementation of the wasi:io package, which is the foundation of
20//! WASIp2. wasmtime-wasi-io provides the Pollable, InputStream, and
21//! OutputStream traits, and this example shows implementations of those
22//! traits for this particular embedding.
23
24use alloc::boxed::Box;
25use alloc::collections::VecDeque;
26use alloc::rc::Rc;
27use alloc::string::{String, ToString};
28use alloc::vec::Vec;
29use core::cell::{Cell, RefCell};
30use core::fmt::Write as _;
31use core::future::Future;
32use core::pin::Pin;
33use core::task::{Context, Poll, Waker};
34use wasmtime::component::{Component, Linker, Resource, ResourceTable};
35use wasmtime::{Engine, Result, Store, bail};
36use wasmtime_wasi_io::{
37    IoView,
38    bytes::Bytes,
39    poll::{DynPollable, Pollable, subscribe},
40    streams::{DynInputStream, DynOutputStream, InputStream, OutputStream},
41};
42
43/// Unlike super::run, its nice to provide some sort of output showing what the
44/// wasi program did while it executed, so this function reports in out_buf
45/// what stdout/stderr prints occurred on success (returns 0), or the error
46/// message on failure (returns != 0).
47#[unsafe(no_mangle)]
48pub unsafe extern "C" fn run_wasi(
49    out_buf: *mut u8,
50    out_size: *mut usize,
51    wasi_component: *const u8,
52    wasi_component_size: usize,
53) -> usize {
54    unsafe {
55        let buf = core::slice::from_raw_parts_mut(out_buf, *out_size);
56        let wasi_component = core::slice::from_raw_parts(wasi_component, wasi_component_size);
57        match run(wasi_component) {
58            Ok(output) => {
59                let len = buf.len().min(output.len());
60                buf[..len].copy_from_slice(&output.as_bytes()[..len]);
61                *out_size = len;
62                return 0;
63            }
64            Err(e) => {
65                let msg = format!("{e:?}");
66                let len = buf.len().min(msg.len());
67                buf[..len].copy_from_slice(&msg.as_bytes()[..len]);
68                *out_size = len;
69                return 1;
70            }
71        }
72    }
73}
74
75fn run(wasi_component: &[u8]) -> Result<String> {
76    let config = super::config();
77    // For future: we could consider turning on fuel in the Config to meter
78    // how long a wasm guest could execute for.
79    let engine = Engine::new(&config)?;
80
81    // Like with modules, we deserialize components into native code:
82    let component = match deserialize(&engine, wasi_component)? {
83        Some(c) => c,
84        None => return Ok("cannot load native code - requires virtual memory".to_string()),
85    };
86
87    // Linker provides wasmtime-wasi-io's implementation of wasi:io package,
88    // and a number of other wasi interfaces implemented below as part of this
89    // example.
90    let mut linker = Linker::new(&engine);
91    wasmtime_wasi_io::add_to_linker_async(&mut linker)?;
92    add_to_linker_async(&mut linker)?;
93
94    // Ensure all imports of the component are satisfied by the linker:
95    let instance_pre = linker.instantiate_pre(&component)?;
96    // Ensure the exports of the component provide the Command world:
97    let command_pre = CommandPre::new(instance_pre)?;
98
99    // Executor and WasiCtx share the same clock:
100    let clock = Clock::new();
101
102    // Use our custom executor to run some async code here:
103    block_on(clock.clone(), async move {
104        let ctx = ExampleCtx {
105            table: ResourceTable::new(),
106            clock,
107            stdout: WriteLog::new(),
108            stderr: WriteLog::new(),
109        };
110        let mut store = Store::new(&engine, ctx);
111        // instantiate runs the wasm `start` section of
112        let instance = command_pre.instantiate_async(&mut store).await?;
113        instance
114            .wasi_cli_run()
115            .call_run(&mut store)
116            .await?
117            .map_err(|()| wasmtime::format_err!("wasi cli run returned error"))?;
118
119        store.into_data().output()
120    })
121}
122
123fn deserialize(engine: &Engine, component: &[u8]) -> Result<Option<Component>> {
124    match unsafe { Component::deserialize(engine, component) } {
125        Ok(component) => Ok(Some(component)),
126        Err(e) => {
127            // Currently if custom signals/virtual memory are disabled then this
128            // example is expected to fail to load since loading native code
129            // requires virtual memory. In the future this will go away as when
130            // signals-based-traps is disabled then that means that the
131            // interpreter should be used which should work here.
132            if !cfg!(feature = "custom")
133                && e.to_string()
134                    .contains("requires virtual memory to be enabled")
135            {
136                Ok(None)
137            } else {
138                Err(e)
139            }
140        }
141    }
142}
143
144// Generate bindings for the entire wasi:cli command world. We won't impl and
145// link with all of these generated bindings for the sake of this example.
146wasmtime::component::bindgen!({
147    path: "../../../crates/wasi/src/p2/wit",
148    world: "wasi:cli/command",
149    imports: { default: trappable },
150    exports: { default: async },
151    require_store_data_send: true,
152    // Important: tell bindgen that anywhere it encounters the wasi:io
153    // package, refer to the bindings generated in the wasmtime_wasi_io crate.
154    // This way, all uses of the streams and pollable in the bindings in this
155    // file match with the resource types (DynInputStream, DynOutputStream,
156    // DynPollable) we use from the wasmtime_wasi_io crate.
157    with: {
158        "wasi:io": wasmtime_wasi_io::bindings::wasi::io,
159    }
160});
161
162/// A Ctx struct particular to this example. In library code designed to be
163/// reused and extended, this might be called a WasiCtx and not include a
164/// ResourceTable as a member, but for the sake of this example, we put
165/// everything that the bind
166pub struct ExampleCtx {
167    table: ResourceTable,
168    clock: Clock,
169    stdout: WriteLog,
170    stderr: WriteLog,
171}
172
173// Provide an IoView impl in order to satisfy
174// wasmtime_wasi_io::add_to_linker_async.
175impl IoView for ExampleCtx {
176    fn table(&mut self) -> &mut ResourceTable {
177        &mut self.table
178    }
179}
180
181impl ExampleCtx {
182    // Collect all of the output written to stdout and stderr into a simple
183    // human-readable string, to be written to out_buf from run_wasi on
184    // success. Lossy utf8 conversion because this is an example.
185    fn output(&self) -> Result<String> {
186        let mut out = String::new();
187        let stdout = self.stdout.log.borrow();
188        if !stdout.is_empty() {
189            write!(&mut out, "stdout:\n")?;
190            for chunk in stdout.iter() {
191                write!(&mut out, "{}", String::from_utf8_lossy(chunk))?;
192            }
193        }
194        let stderr = self.stderr.log.borrow();
195        if !stderr.is_empty() {
196            write!(&mut out, "stderr:\n")?;
197            for chunk in stderr.iter() {
198                write!(&mut out, "{}", String::from_utf8_lossy(chunk))?;
199            }
200        }
201        Ok(out)
202    }
203}
204
205// Add the minimum number of wasi interfaces to the Linker to instantiate the
206// example application. This does not provide support for the entire
207// wasi:cli/command world. Many of these impls are bare-bones and some are
208// intentionally broken, see notes below.
209pub fn add_to_linker_async(linker: &mut Linker<ExampleCtx>) -> Result<()> {
210    type Data = wasmtime::component::HasSelf<ExampleCtx>;
211
212    wasi::clocks::monotonic_clock::add_to_linker::<_, Data>(linker, |t| t)?;
213    wasi::clocks::wall_clock::add_to_linker::<_, Data>(linker, |t| t)?;
214    wasi::cli::environment::add_to_linker::<_, Data>(linker, |t| t)?;
215    wasi::cli::exit::add_to_linker::<_, Data>(linker, &Default::default(), |t| t)?;
216    wasi::cli::stdin::add_to_linker::<_, Data>(linker, |t| t)?;
217    wasi::cli::stdout::add_to_linker::<_, Data>(linker, |t| t)?;
218    wasi::cli::stderr::add_to_linker::<_, Data>(linker, |t| t)?;
219    wasi::random::random::add_to_linker::<_, Data>(linker, |t| t)?;
220    wasi::filesystem::preopens::add_to_linker::<_, Data>(linker, |t| t)?;
221    wasi::filesystem::types::add_to_linker::<_, Data>(linker, |t| t)?;
222    Ok(())
223}
224
225// WasiCtx and the Executor need to share a single clock, so make it reference
226// counted.
227#[derive(Clone)]
228struct Clock(Rc<Cell<u64>>);
229impl Clock {
230    fn new() -> Self {
231        Clock(Rc::new(Cell::new(0)))
232    }
233    fn get(&self) -> u64 {
234        self.0.get()
235    }
236    fn set(&self, to: u64) {
237        self.0.set(to)
238    }
239    fn timer(&self, due: u64) -> Deadline {
240        Deadline {
241            clock: self.clone(),
242            due,
243        }
244    }
245}
246// SAFETY: only will consume this crate in single-threaded environment
247unsafe impl Send for Clock {}
248unsafe impl Sync for Clock {}
249
250// A Deadline is used to implement the monotonic clock's pollable. It is a
251// future which is ready when the clock reaches the due time.
252#[derive(Clone)]
253struct Deadline {
254    clock: Clock,
255    due: u64,
256}
257impl Future for Deadline {
258    type Output = ();
259    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
260        let now = self.clock.get();
261        if now < self.due {
262            Executor::current().push_deadline(self.due, cx.waker().clone());
263            Poll::Pending
264        } else {
265            Poll::Ready(())
266        }
267    }
268}
269#[wasmtime_wasi_io::async_trait]
270impl Pollable for Deadline {
271    async fn ready(&mut self) {
272        self.clone().await
273    }
274}
275
276// An input-stream which is never ready for reading is used to implement
277// stdin.
278struct NeverReadable;
279#[wasmtime_wasi_io::async_trait]
280impl Pollable for NeverReadable {
281    async fn ready(&mut self) {
282        struct Pending;
283        impl Future for Pending {
284            type Output = ();
285            fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
286                Poll::Pending
287            }
288        }
289        Pending.await
290    }
291}
292impl InputStream for NeverReadable {
293    fn read(&mut self, _: usize) -> wasmtime_wasi_io::streams::StreamResult<Bytes> {
294        unreachable!("never ready for reading")
295    }
296}
297
298// WriteLog is used implement stdout and stderr. Cloneable because wasi:cli
299// requires, when calling get_stdout/get_stderr multiple times, to provide
300// distinct resources that point to the same underlying stream. RefCell
301// provides mutation, and VecDeque provides O(1) push_back operation.
302#[derive(Clone)]
303struct WriteLog {
304    log: Rc<RefCell<VecDeque<Bytes>>>,
305}
306impl WriteLog {
307    fn new() -> Self {
308        Self {
309            log: Rc::new(RefCell::new(VecDeque::new())),
310        }
311    }
312}
313// SAFETY: only will consume this crate in single-threaded environment
314unsafe impl Send for WriteLog {}
315unsafe impl Sync for WriteLog {}
316
317impl OutputStream for WriteLog {
318    fn check_write(&mut self) -> wasmtime_wasi_io::streams::StreamResult<usize> {
319        Ok(usize::MAX)
320    }
321    fn write(&mut self, contents: Bytes) -> wasmtime_wasi_io::streams::StreamResult<()> {
322        self.log.borrow_mut().push_back(contents);
323        Ok(())
324    }
325    fn flush(&mut self) -> wasmtime_wasi_io::streams::StreamResult<()> {
326        Ok(())
327    }
328}
329#[wasmtime_wasi_io::async_trait]
330impl Pollable for WriteLog {
331    async fn ready(&mut self) {
332        // always ready - return immediately.
333    }
334}
335
336// Global symbol (no thread local storage on this target) provides ability for
337// Future impls to tell the Executor what they are waiting on.
338static EXECUTOR: ExecutorGlobal = ExecutorGlobal::new();
339
340// RefCell for mutation, Option so the Executor can be present only for life
341// of the block_on call.
342struct ExecutorGlobal(RefCell<Option<Executor>>);
343impl ExecutorGlobal {
344    const fn new() -> Self {
345        ExecutorGlobal(RefCell::new(None))
346    }
347}
348// SAFETY: only will consume this crate in single-threaded environment
349unsafe impl Send for ExecutorGlobal {}
350unsafe impl Sync for ExecutorGlobal {}
351
352// Rc because executor and global both need to hold a reference, and makes it
353// convenient to implement current(). RefCell for mutation.
354struct Executor(Rc<RefCell<ExecutorInner>>);
355
356impl Executor {
357    pub fn new() -> Self {
358        Executor(Rc::new(RefCell::new(ExecutorInner {
359            schedule: Vec::new(),
360        })))
361    }
362    pub fn current() -> Self {
363        Executor(
364            EXECUTOR
365                .0
366                .borrow_mut()
367                .as_ref()
368                .expect("Executor::current must be called within block_on")
369                .0
370                .clone(),
371        )
372    }
373    pub fn push_deadline(&mut self, due: u64, waker: Waker) {
374        self.0.borrow_mut().schedule.push((due, waker))
375    }
376}
377
378// Schedule, as provided by the Deadline future impls. Map of due times to
379// wakers.
380struct ExecutorInner {
381    schedule: Vec<(u64, Waker)>,
382}
383
384impl ExecutorInner {
385    // Get the earliest deadline currently waiting. None if there are no
386    // deadlines.
387    fn earliest_deadline(&self) -> Option<u64> {
388        self.schedule.iter().map(|(due, _)| due).min().copied()
389    }
390    // Return all wakers associated with deadlines before or equal to the
391    // current clock time. Removes the wakers and their deadline from the
392    // schedule.
393    fn ready_deadlines(&mut self, now: u64) -> Vec<Waker> {
394        let mut i = 0;
395        let mut wakers = Vec::new();
396        // This is basically https://doc.rust-lang.org/std/vec/struct.Vec.html#method.extract_if,
397        // which is unstable
398        while i < self.schedule.len() {
399            if let Some((due, _)) = self.schedule.get(i) {
400                if *due <= now {
401                    let (_, waker) = self.schedule.remove(i);
402                    wakers.push(waker);
403                } else {
404                    i += 1;
405                }
406            } else {
407                break;
408            }
409        }
410        wakers
411    }
412}
413
414fn block_on<R>(clock: Clock, f: impl Future<Output = Result<R>> + Send + 'static) -> Result<R> {
415    // Guard against nested invocations
416    if EXECUTOR.0.borrow_mut().is_some() {
417        panic!("cannot block_on while executor is running!")
418    }
419    let executor = Executor::new();
420    *EXECUTOR.0.borrow_mut() = Some(Executor(executor.0.clone()));
421
422    // No special waker needed for this executor.
423    let mut cx = Context::from_waker(Waker::noop());
424    let mut f = core::pin::pin!(f);
425
426    // Drive the Future to completion in the following loop
427    let r = 'outer: loop {
428        // Arbitrary. Could be as little as 1. There's no fuel-based async
429        // yielding in this example so repeated polls is probably not making
430        // progress without "providing input" from the outside environment,
431        // below.
432        const POLLS_PER_CLOCK: usize = 200;
433        for _ in 0..POLLS_PER_CLOCK {
434            match f.as_mut().poll(&mut cx) {
435                Poll::Pending => {}
436                Poll::Ready(r) => break 'outer r,
437            }
438        }
439
440        // This is where a non-example executor would wait for input from the
441        // "outside world". This example checks if the schedule indicates the
442        // guest is waiting on some future deadline and fast-forwards time
443        // until then, because no other input is possible in this example.
444        if let Some(sleep_until) = executor.0.borrow().earliest_deadline() {
445            clock.set(sleep_until);
446        } else {
447            clock.set(clock.get() + 1);
448        }
449
450        // Any wakers which are ready can be waked now.
451        for waker in executor.0.borrow_mut().ready_deadlines(clock.get()) {
452            waker.wake()
453        }
454    };
455
456    // Clean up guard for nested invocations
457    let _ = EXECUTOR
458        .0
459        .borrow_mut()
460        .take()
461        .expect("executor vacated global while running");
462    r
463}
464
465// -------------- impls for the bindgen! Host traits ------------------
466// These impls are written directly for WasiCtx, which is fine because this
467// example isn't trying to create reusable library code.
468
469impl wasi::clocks::monotonic_clock::Host for ExampleCtx {
470    fn now(&mut self) -> Result<wasi::clocks::monotonic_clock::Instant> {
471        Ok(self.clock.get())
472    }
473    fn resolution(&mut self) -> Result<wasi::clocks::monotonic_clock::Duration> {
474        Ok(1)
475    }
476    fn subscribe_duration(
477        &mut self,
478        duration: wasi::clocks::monotonic_clock::Duration,
479    ) -> Result<Resource<DynPollable>> {
480        self.subscribe_instant(self.clock.get() + duration)
481    }
482    fn subscribe_instant(
483        &mut self,
484        deadline: wasi::clocks::monotonic_clock::Instant,
485    ) -> Result<Resource<DynPollable>> {
486        let timer = self.clock.timer(deadline);
487        let deadline = self.table().push(timer)?;
488        Ok(subscribe(self.table(), deadline)?)
489    }
490}
491
492impl wasi::clocks::wall_clock::Host for ExampleCtx {
493    fn now(&mut self) -> Result<wasi::clocks::wall_clock::Datetime> {
494        // A bogus time. This datetime is relative to the unix epoch. Just
495        // reuse the monotonic time for the sake of the example.
496        let now = self.clock.get();
497        let seconds = now / 1_000_000_000;
498        let nanoseconds = (now - (seconds * 1_000_000_000)) as u32;
499        Ok(wasi::clocks::wall_clock::Datetime {
500            seconds,
501            nanoseconds,
502        })
503    }
504    fn resolution(&mut self) -> Result<wasi::clocks::wall_clock::Datetime> {
505        Ok(wasi::clocks::wall_clock::Datetime {
506            seconds: 0,
507            nanoseconds: 1,
508        })
509    }
510}
511
512// No arguments, environment variables, or cwd are provided.
513impl wasi::cli::environment::Host for ExampleCtx {
514    fn get_arguments(&mut self) -> Result<Vec<String>> {
515        Ok(Vec::new())
516    }
517    fn get_environment(&mut self) -> Result<Vec<(String, String)>> {
518        Ok(Vec::new())
519    }
520    fn initial_cwd(&mut self) -> Result<Option<String>> {
521        Ok(None)
522    }
523}
524
525// Ideally this would follow the example in wasmtime-wasi: make a struct, impl
526// Error on it, and try downcasting to it at the call_run site to see if the
527// wasi:cli/exit was used to exit with success without unwinding - valid but
528// uncommon behavior that should be treated the same as returning ok from the
529// wasi:cli/run.run function. Our example program doesn't exit that way.
530impl wasi::cli::exit::Host for ExampleCtx {
531    fn exit(&mut self, code: Result<(), ()>) -> Result<()> {
532        if code.is_ok() {
533            bail!("wasi exit success")
534        } else {
535            bail!("wasi exit error")
536        }
537    }
538    // This is feature-flagged (unstable) in the wits. Per the LinkOptions
539    // passed to the wasi::cli::exit::add_to_linker, it won't be found in
540    // any guest code.
541    fn exit_with_code(&mut self, _: u8) -> Result<()> {
542        unreachable!("this unstable func is not added to the linker");
543    }
544}
545
546impl wasi::cli::stdin::Host for ExampleCtx {
547    fn get_stdin(&mut self) -> Result<Resource<DynInputStream>> {
548        let stdin: DynInputStream = Box::new(NeverReadable);
549        Ok(self.table().push(stdin)?)
550    }
551}
552
553impl wasi::cli::stdout::Host for ExampleCtx {
554    fn get_stdout(&mut self) -> Result<Resource<DynOutputStream>> {
555        let stdout: DynOutputStream = Box::new(self.stdout.clone());
556        Ok(self.table().push(stdout)?)
557    }
558}
559
560impl wasi::cli::stderr::Host for ExampleCtx {
561    fn get_stderr(&mut self) -> Result<Resource<DynOutputStream>> {
562        let stderr: DynOutputStream = Box::new(self.stderr.clone());
563        Ok(self.table().push(stderr)?)
564    }
565}
566
567// This is obviously bogus and breaks the guarantees given by this interface.
568// In a real embedding, provide a high quality source of randomness here.
569impl wasi::random::random::Host for ExampleCtx {
570    fn get_random_bytes(&mut self, len: u64) -> Result<Vec<u8>> {
571        let mut vec = Vec::new();
572        vec.resize(len as usize, 0u8);
573        Ok(vec)
574    }
575    fn get_random_u64(&mut self) -> Result<u64> {
576        Ok(0)
577    }
578}
579
580// The preopens are the only place the filesystem is provided a Descriptor,
581// from which to try open_at to get more Descriptors. If we don't provide
582// anything here, none of the methods on Descriptor will ever be reachable,
583// because Resources are unforgable (the runtime will trap bogus indexes).
584impl wasi::filesystem::preopens::Host for ExampleCtx {
585    fn get_directories(
586        &mut self,
587    ) -> Result<Vec<(Resource<wasi::filesystem::types::Descriptor>, String)>> {
588        // Never construct a Descriptor, so all of the bails in the rest of Filesystem should be
589        // unreachable.
590        Ok(Vec::new())
591    }
592}
593
594// This impl is completely empty!
595impl wasi::filesystem::types::HostDescriptor for ExampleCtx {
596    fn read_via_stream(
597        &mut self,
598        _: Resource<wasi::filesystem::types::Descriptor>,
599        _: u64,
600    ) -> Result<Result<Resource<DynInputStream>, wasi::filesystem::types::ErrorCode>> {
601        unreachable!("no filesystem")
602    }
603    fn write_via_stream(
604        &mut self,
605        _: Resource<wasi::filesystem::types::Descriptor>,
606        _: u64,
607    ) -> Result<Result<Resource<DynOutputStream>, wasi::filesystem::types::ErrorCode>> {
608        unreachable!("no filesystem")
609    }
610    fn append_via_stream(
611        &mut self,
612        _: Resource<wasi::filesystem::types::Descriptor>,
613    ) -> Result<Result<Resource<DynOutputStream>, wasi::filesystem::types::ErrorCode>> {
614        unreachable!("no filesystem")
615    }
616    fn advise(
617        &mut self,
618        _: Resource<wasi::filesystem::types::Descriptor>,
619        _: u64,
620        _: u64,
621        _: wasi::filesystem::types::Advice,
622    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
623        unreachable!("no filesystem")
624    }
625    fn sync_data(
626        &mut self,
627        _: Resource<wasi::filesystem::types::Descriptor>,
628    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
629        unreachable!("no filesystem")
630    }
631    fn get_flags(
632        &mut self,
633        _: Resource<wasi::filesystem::types::Descriptor>,
634    ) -> Result<Result<wasi::filesystem::types::DescriptorFlags, wasi::filesystem::types::ErrorCode>>
635    {
636        unreachable!("no filesystem")
637    }
638    fn get_type(
639        &mut self,
640        _: Resource<wasi::filesystem::types::Descriptor>,
641    ) -> Result<Result<wasi::filesystem::types::DescriptorType, wasi::filesystem::types::ErrorCode>>
642    {
643        unreachable!("no filesystem")
644    }
645    fn set_size(
646        &mut self,
647        _: Resource<wasi::filesystem::types::Descriptor>,
648        _: u64,
649    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
650        unreachable!("no filesystem")
651    }
652    fn set_times(
653        &mut self,
654        _: Resource<wasi::filesystem::types::Descriptor>,
655        _: wasi::filesystem::types::NewTimestamp,
656        _: wasi::filesystem::types::NewTimestamp,
657    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
658        unreachable!("no filesystem")
659    }
660    fn read(
661        &mut self,
662        _: Resource<wasi::filesystem::types::Descriptor>,
663        _: u64,
664        _: u64,
665    ) -> Result<Result<(Vec<u8>, bool), wasi::filesystem::types::ErrorCode>> {
666        unreachable!("no filesystem")
667    }
668    fn write(
669        &mut self,
670        _: Resource<wasi::filesystem::types::Descriptor>,
671        _: Vec<u8>,
672        _: u64,
673    ) -> Result<Result<u64, wasi::filesystem::types::ErrorCode>> {
674        unreachable!("no filesystem")
675    }
676
677    fn read_directory(
678        &mut self,
679        _: Resource<wasi::filesystem::types::Descriptor>,
680    ) -> Result<
681        Result<
682            Resource<wasi::filesystem::types::DirectoryEntryStream>,
683            wasi::filesystem::types::ErrorCode,
684        >,
685    > {
686        unreachable!("no filesystem")
687    }
688    fn sync(
689        &mut self,
690        _: Resource<wasi::filesystem::types::Descriptor>,
691    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
692        unreachable!("no filesystem")
693    }
694    fn create_directory_at(
695        &mut self,
696        _: Resource<wasi::filesystem::types::Descriptor>,
697        _: String,
698    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
699        unreachable!("no filesystem")
700    }
701    fn stat(
702        &mut self,
703        _: Resource<wasi::filesystem::types::Descriptor>,
704    ) -> Result<Result<wasi::filesystem::types::DescriptorStat, wasi::filesystem::types::ErrorCode>>
705    {
706        unreachable!("no filesystem")
707    }
708    fn stat_at(
709        &mut self,
710        _: Resource<wasi::filesystem::types::Descriptor>,
711        _: wasi::filesystem::types::PathFlags,
712        _: String,
713    ) -> Result<Result<wasi::filesystem::types::DescriptorStat, wasi::filesystem::types::ErrorCode>>
714    {
715        unreachable!("no filesystem")
716    }
717    fn set_times_at(
718        &mut self,
719        _: Resource<wasi::filesystem::types::Descriptor>,
720        _: wasi::filesystem::types::PathFlags,
721        _: String,
722        _: wasi::filesystem::types::NewTimestamp,
723        _: wasi::filesystem::types::NewTimestamp,
724    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
725        unreachable!("no filesystem")
726    }
727    fn link_at(
728        &mut self,
729        _: Resource<wasi::filesystem::types::Descriptor>,
730        _: wasi::filesystem::types::PathFlags,
731        _: String,
732        _: Resource<wasi::filesystem::types::Descriptor>,
733        _: String,
734    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
735        unreachable!("no filesystem")
736    }
737    fn open_at(
738        &mut self,
739        _: Resource<wasi::filesystem::types::Descriptor>,
740        _: wasi::filesystem::types::PathFlags,
741        _: String,
742        _: wasi::filesystem::types::OpenFlags,
743        _: wasi::filesystem::types::DescriptorFlags,
744    ) -> Result<
745        Result<Resource<wasi::filesystem::types::Descriptor>, wasi::filesystem::types::ErrorCode>,
746    > {
747        unreachable!("no filesystem")
748    }
749    fn readlink_at(
750        &mut self,
751        _: Resource<wasi::filesystem::types::Descriptor>,
752        _: String,
753    ) -> Result<Result<String, wasi::filesystem::types::ErrorCode>> {
754        unreachable!("no filesystem")
755    }
756    fn remove_directory_at(
757        &mut self,
758        _: Resource<wasi::filesystem::types::Descriptor>,
759        _: String,
760    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
761        unreachable!("no filesystem")
762    }
763    fn rename_at(
764        &mut self,
765        _: Resource<wasi::filesystem::types::Descriptor>,
766        _: String,
767        _: Resource<wasi::filesystem::types::Descriptor>,
768        _: String,
769    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
770        unreachable!("no filesystem")
771    }
772    fn symlink_at(
773        &mut self,
774        _: Resource<wasi::filesystem::types::Descriptor>,
775        _: String,
776        _: String,
777    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
778        unreachable!("no filesystem")
779    }
780    fn unlink_file_at(
781        &mut self,
782        _: Resource<wasi::filesystem::types::Descriptor>,
783        _: String,
784    ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
785        unreachable!("no filesystem")
786    }
787    fn is_same_object(
788        &mut self,
789        _: Resource<wasi::filesystem::types::Descriptor>,
790        _: Resource<wasi::filesystem::types::Descriptor>,
791    ) -> Result<bool> {
792        unreachable!("no filesystem")
793    }
794    fn metadata_hash(
795        &mut self,
796        _: Resource<wasi::filesystem::types::Descriptor>,
797    ) -> Result<
798        Result<wasi::filesystem::types::MetadataHashValue, wasi::filesystem::types::ErrorCode>,
799    > {
800        unreachable!("no filesystem")
801    }
802    fn metadata_hash_at(
803        &mut self,
804        _: Resource<wasi::filesystem::types::Descriptor>,
805        _: wasi::filesystem::types::PathFlags,
806        _: String,
807    ) -> Result<
808        Result<wasi::filesystem::types::MetadataHashValue, wasi::filesystem::types::ErrorCode>,
809    > {
810        unreachable!("no filesystem")
811    }
812
813    fn drop(&mut self, _: Resource<wasi::filesystem::types::Descriptor>) -> Result<()> {
814        unreachable!("no filesystem")
815    }
816}
817// Only place this resource can be created is with Descriptor::read_directory,
818// so this will never be constructed either.
819impl wasi::filesystem::types::HostDirectoryEntryStream for ExampleCtx {
820    fn read_directory_entry(
821        &mut self,
822        _: Resource<wasi::filesystem::types::DirectoryEntryStream>,
823    ) -> Result<
824        Result<Option<wasi::filesystem::types::DirectoryEntry>, wasi::filesystem::types::ErrorCode>,
825    > {
826        unreachable!("no filesystem")
827    }
828    fn drop(&mut self, _: Resource<wasi::filesystem::types::DirectoryEntryStream>) -> Result<()> {
829        unreachable!("no filesystem")
830    }
831}
832
833// No stream is ever constructed from a Descriptor, there will never be a
834// valid downcast of a stream error into a filesystem error-code.
835impl wasi::filesystem::types::Host for ExampleCtx {
836    fn filesystem_error_code(
837        &mut self,
838        _: Resource<wasmtime_wasi_io::streams::Error>,
839    ) -> Result<Option<wasi::filesystem::types::ErrorCode>> {
840        Ok(None)
841    }
842}