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