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