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