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