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