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