1use alloc::boxed::Box;
25use alloc::collections::VecDeque;
26use alloc::rc::Rc;
27use alloc::string::{String, ToString};
28use alloc::vec::Vec;
29use anyhow::{Result, bail};
30use core::cell::{Cell, RefCell};
31use core::fmt::Write as _;
32use core::future::Future;
33use core::pin::Pin;
34use core::task::{Context, Poll, Waker};
35use wasmtime::component::{Component, Linker, Resource, ResourceTable};
36use wasmtime::{Config, Engine, Store};
37use wasmtime_wasi_io::{
38 IoView,
39 bytes::Bytes,
40 poll::{DynPollable, Pollable, subscribe},
41 streams::{DynInputStream, DynOutputStream, InputStream, OutputStream},
42};
43
44#[unsafe(no_mangle)]
49pub unsafe extern "C" fn run_wasi(
50 out_buf: *mut u8,
51 out_size: *mut usize,
52 wasi_component: *const u8,
53 wasi_component_size: usize,
54) -> usize {
55 unsafe {
56 let buf = core::slice::from_raw_parts_mut(out_buf, *out_size);
57 let wasi_component = core::slice::from_raw_parts(wasi_component, wasi_component_size);
58 match run(wasi_component) {
59 Ok(output) => {
60 let len = buf.len().min(output.len());
61 buf[..len].copy_from_slice(&output.as_bytes()[..len]);
62 *out_size = len;
63 return 0;
64 }
65 Err(e) => {
66 let msg = format!("{e:?}");
67 let len = buf.len().min(msg.len());
68 buf[..len].copy_from_slice(&msg.as_bytes()[..len]);
69 *out_size = len;
70 return 1;
71 }
72 }
73 }
74}
75
76fn run(wasi_component: &[u8]) -> Result<String> {
77 let mut config = Config::default();
82 config.async_support(true);
83 let engine = Engine::new(&config)?;
86
87 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 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 let instance_pre = linker.instantiate_pre(&component)?;
102 let command_pre = CommandPre::new(instance_pre)?;
104
105 let clock = Clock::new();
107
108 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 let instance = command_pre.instantiate_async(&mut store).await?;
119 instance
120 .wasi_cli_run()
121 .call_run(&mut store)
122 .await?
123 .map_err(|()| anyhow::anyhow!("wasi cli run returned error"))?;
124
125 store.into_data().output()
126 })
127}
128
129fn deserialize(engine: &Engine, component: &[u8]) -> Result<Option<Component>> {
130 match unsafe { Component::deserialize(engine, component) } {
131 Ok(component) => Ok(Some(component)),
132 Err(e) => {
133 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
150wasmtime::component::bindgen!({
153 path: "../../../crates/wasi/src/p2/wit",
154 world: "wasi:cli/command",
155 async: { only_imports: [] },
156 trappable_imports: true,
157 with: {
163 "wasi:io": wasmtime_wasi_io::bindings::wasi::io,
164 }
165});
166
167pub struct ExampleCtx {
172 table: ResourceTable,
173 clock: Clock,
174 stdout: WriteLog,
175 stderr: WriteLog,
176}
177
178impl IoView for ExampleCtx {
181 fn table(&mut self) -> &mut ResourceTable {
182 &mut self.table
183 }
184}
185
186impl ExampleCtx {
187 fn output(&self) -> Result<String> {
191 let mut out = String::new();
192 let stdout = self.stdout.log.borrow();
193 if !stdout.is_empty() {
194 write!(&mut out, "stdout:\n")?;
195 for chunk in stdout.iter() {
196 write!(&mut out, "{}", String::from_utf8_lossy(chunk))?;
197 }
198 }
199 let stderr = self.stderr.log.borrow();
200 if !stderr.is_empty() {
201 write!(&mut out, "stderr:\n")?;
202 for chunk in stderr.iter() {
203 write!(&mut out, "{}", String::from_utf8_lossy(chunk))?;
204 }
205 }
206 Ok(out)
207 }
208}
209
210pub fn add_to_linker_async(linker: &mut Linker<ExampleCtx>) -> Result<()> {
215 type Data = wasmtime::component::HasSelf<ExampleCtx>;
216
217 wasi::clocks::monotonic_clock::add_to_linker::<_, Data>(linker, |t| t)?;
218 wasi::clocks::wall_clock::add_to_linker::<_, Data>(linker, |t| t)?;
219 wasi::cli::environment::add_to_linker::<_, Data>(linker, |t| t)?;
220 wasi::cli::exit::add_to_linker::<_, Data>(linker, &Default::default(), |t| t)?;
221 wasi::cli::stdin::add_to_linker::<_, Data>(linker, |t| t)?;
222 wasi::cli::stdout::add_to_linker::<_, Data>(linker, |t| t)?;
223 wasi::cli::stderr::add_to_linker::<_, Data>(linker, |t| t)?;
224 wasi::random::random::add_to_linker::<_, Data>(linker, |t| t)?;
225 wasi::filesystem::preopens::add_to_linker::<_, Data>(linker, |t| t)?;
226 wasi::filesystem::types::add_to_linker::<_, Data>(linker, |t| t)?;
227 Ok(())
228}
229
230#[derive(Clone)]
233struct Clock(Rc<Cell<u64>>);
234impl Clock {
235 fn new() -> Self {
236 Clock(Rc::new(Cell::new(0)))
237 }
238 fn get(&self) -> u64 {
239 self.0.get()
240 }
241 fn set(&self, to: u64) {
242 self.0.set(to)
243 }
244 fn timer(&self, due: u64) -> Deadline {
245 Deadline {
246 clock: self.clone(),
247 due,
248 }
249 }
250}
251unsafe impl Send for Clock {}
253unsafe impl Sync for Clock {}
254
255#[derive(Clone)]
258struct Deadline {
259 clock: Clock,
260 due: u64,
261}
262impl Future for Deadline {
263 type Output = ();
264 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
265 let now = self.clock.get();
266 if now < self.due {
267 Executor::current().push_deadline(self.due, cx.waker().clone());
268 Poll::Pending
269 } else {
270 Poll::Ready(())
271 }
272 }
273}
274#[wasmtime_wasi_io::async_trait]
275impl Pollable for Deadline {
276 async fn ready(&mut self) {
277 self.clone().await
278 }
279}
280
281struct NeverReadable;
284#[wasmtime_wasi_io::async_trait]
285impl Pollable for NeverReadable {
286 async fn ready(&mut self) {
287 struct Pending;
288 impl Future for Pending {
289 type Output = ();
290 fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
291 Poll::Pending
292 }
293 }
294 Pending.await
295 }
296}
297impl InputStream for NeverReadable {
298 fn read(&mut self, _: usize) -> wasmtime_wasi_io::streams::StreamResult<Bytes> {
299 unreachable!("never ready for reading")
300 }
301}
302
303#[derive(Clone)]
308struct WriteLog {
309 log: Rc<RefCell<VecDeque<Bytes>>>,
310}
311impl WriteLog {
312 fn new() -> Self {
313 Self {
314 log: Rc::new(RefCell::new(VecDeque::new())),
315 }
316 }
317}
318unsafe impl Send for WriteLog {}
320unsafe impl Sync for WriteLog {}
321
322impl OutputStream for WriteLog {
323 fn check_write(&mut self) -> wasmtime_wasi_io::streams::StreamResult<usize> {
324 Ok(usize::MAX)
325 }
326 fn write(&mut self, contents: Bytes) -> wasmtime_wasi_io::streams::StreamResult<()> {
327 self.log.borrow_mut().push_back(contents);
328 Ok(())
329 }
330 fn flush(&mut self) -> wasmtime_wasi_io::streams::StreamResult<()> {
331 Ok(())
332 }
333}
334#[wasmtime_wasi_io::async_trait]
335impl Pollable for WriteLog {
336 async fn ready(&mut self) {
337 }
339}
340
341static EXECUTOR: ExecutorGlobal = ExecutorGlobal::new();
344
345struct ExecutorGlobal(RefCell<Option<Executor>>);
348impl ExecutorGlobal {
349 const fn new() -> Self {
350 ExecutorGlobal(RefCell::new(None))
351 }
352}
353unsafe impl Send for ExecutorGlobal {}
355unsafe impl Sync for ExecutorGlobal {}
356
357struct Executor(Rc<RefCell<ExecutorInner>>);
360
361impl Executor {
362 pub fn new() -> Self {
363 Executor(Rc::new(RefCell::new(ExecutorInner {
364 schedule: Vec::new(),
365 })))
366 }
367 pub fn current() -> Self {
368 Executor(
369 EXECUTOR
370 .0
371 .borrow_mut()
372 .as_ref()
373 .expect("Executor::current must be called within block_on")
374 .0
375 .clone(),
376 )
377 }
378 pub fn push_deadline(&mut self, due: u64, waker: Waker) {
379 self.0.borrow_mut().schedule.push((due, waker))
380 }
381}
382
383struct ExecutorInner {
386 schedule: Vec<(u64, Waker)>,
387}
388
389impl ExecutorInner {
390 fn earliest_deadline(&self) -> Option<u64> {
393 self.schedule.iter().map(|(due, _)| due).min().copied()
394 }
395 fn ready_deadlines(&mut self, now: u64) -> Vec<Waker> {
399 let mut i = 0;
400 let mut wakers = Vec::new();
401 while i < self.schedule.len() {
404 if let Some((due, _)) = self.schedule.get(i) {
405 if *due <= now {
406 let (_, waker) = self.schedule.remove(i);
407 wakers.push(waker);
408 } else {
409 i += 1;
410 }
411 } else {
412 break;
413 }
414 }
415 wakers
416 }
417}
418
419fn block_on<R>(clock: Clock, f: impl Future<Output = Result<R>> + Send + 'static) -> Result<R> {
420 if EXECUTOR.0.borrow_mut().is_some() {
422 panic!("cannot block_on while executor is running!")
423 }
424 let executor = Executor::new();
425 *EXECUTOR.0.borrow_mut() = Some(Executor(executor.0.clone()));
426
427 let mut cx = Context::from_waker(Waker::noop());
429 let mut f = core::pin::pin!(f);
430
431 let r = 'outer: loop {
433 const POLLS_PER_CLOCK: usize = 200;
438 for _ in 0..POLLS_PER_CLOCK {
439 match f.as_mut().poll(&mut cx) {
440 Poll::Pending => {}
441 Poll::Ready(r) => break 'outer r,
442 }
443 }
444
445 if let Some(sleep_until) = executor.0.borrow().earliest_deadline() {
450 clock.set(sleep_until);
451 } else {
452 clock.set(clock.get() + 1);
453 }
454
455 for waker in executor.0.borrow_mut().ready_deadlines(clock.get()) {
457 waker.wake()
458 }
459 };
460
461 let _ = EXECUTOR
463 .0
464 .borrow_mut()
465 .take()
466 .expect("executor vacated global while running");
467 r
468}
469
470impl wasi::clocks::monotonic_clock::Host for ExampleCtx {
475 fn now(&mut self) -> Result<wasi::clocks::monotonic_clock::Instant> {
476 Ok(self.clock.get())
477 }
478 fn resolution(&mut self) -> Result<wasi::clocks::monotonic_clock::Duration> {
479 Ok(1)
480 }
481 fn subscribe_duration(
482 &mut self,
483 duration: wasi::clocks::monotonic_clock::Duration,
484 ) -> Result<Resource<DynPollable>> {
485 self.subscribe_instant(self.clock.get() + duration)
486 }
487 fn subscribe_instant(
488 &mut self,
489 deadline: wasi::clocks::monotonic_clock::Instant,
490 ) -> Result<Resource<DynPollable>> {
491 let timer = self.clock.timer(deadline);
492 let deadline = self.table().push(timer)?;
493 Ok(subscribe(self.table(), deadline)?)
494 }
495}
496
497impl wasi::clocks::wall_clock::Host for ExampleCtx {
498 fn now(&mut self) -> Result<wasi::clocks::wall_clock::Datetime> {
499 let now = self.clock.get();
502 let seconds = now / 1_000_000_000;
503 let nanoseconds = (now - (seconds * 1_000_000_000)) as u32;
504 Ok(wasi::clocks::wall_clock::Datetime {
505 seconds,
506 nanoseconds,
507 })
508 }
509 fn resolution(&mut self) -> Result<wasi::clocks::wall_clock::Datetime> {
510 Ok(wasi::clocks::wall_clock::Datetime {
511 seconds: 0,
512 nanoseconds: 1,
513 })
514 }
515}
516
517impl wasi::cli::environment::Host for ExampleCtx {
519 fn get_arguments(&mut self) -> Result<Vec<String>> {
520 Ok(Vec::new())
521 }
522 fn get_environment(&mut self) -> Result<Vec<(String, String)>> {
523 Ok(Vec::new())
524 }
525 fn initial_cwd(&mut self) -> Result<Option<String>> {
526 Ok(None)
527 }
528}
529
530impl wasi::cli::exit::Host for ExampleCtx {
536 fn exit(&mut self, code: Result<(), ()>) -> Result<()> {
537 if code.is_ok() {
538 bail!("wasi exit success")
539 } else {
540 bail!("wasi exit error")
541 }
542 }
543 fn exit_with_code(&mut self, _: u8) -> Result<()> {
547 unreachable!("this unstable func is not added to the linker");
548 }
549}
550
551impl wasi::cli::stdin::Host for ExampleCtx {
552 fn get_stdin(&mut self) -> Result<Resource<DynInputStream>> {
553 let stdin: DynInputStream = Box::new(NeverReadable);
554 Ok(self.table().push(stdin)?)
555 }
556}
557
558impl wasi::cli::stdout::Host for ExampleCtx {
559 fn get_stdout(&mut self) -> Result<Resource<DynOutputStream>> {
560 let stdout: DynOutputStream = Box::new(self.stdout.clone());
561 Ok(self.table().push(stdout)?)
562 }
563}
564
565impl wasi::cli::stderr::Host for ExampleCtx {
566 fn get_stderr(&mut self) -> Result<Resource<DynOutputStream>> {
567 let stderr: DynOutputStream = Box::new(self.stderr.clone());
568 Ok(self.table().push(stderr)?)
569 }
570}
571
572impl wasi::random::random::Host for ExampleCtx {
575 fn get_random_bytes(&mut self, len: u64) -> Result<Vec<u8>> {
576 let mut vec = Vec::new();
577 vec.resize(len as usize, 0u8);
578 Ok(vec)
579 }
580 fn get_random_u64(&mut self) -> Result<u64> {
581 Ok(0)
582 }
583}
584
585impl wasi::filesystem::preopens::Host for ExampleCtx {
590 fn get_directories(
591 &mut self,
592 ) -> Result<Vec<(Resource<wasi::filesystem::types::Descriptor>, String)>> {
593 Ok(Vec::new())
596 }
597}
598
599impl wasi::filesystem::types::HostDescriptor for ExampleCtx {
601 fn read_via_stream(
602 &mut self,
603 _: Resource<wasi::filesystem::types::Descriptor>,
604 _: u64,
605 ) -> Result<Result<Resource<DynInputStream>, wasi::filesystem::types::ErrorCode>> {
606 unreachable!("no filesystem")
607 }
608 fn write_via_stream(
609 &mut self,
610 _: Resource<wasi::filesystem::types::Descriptor>,
611 _: u64,
612 ) -> Result<Result<Resource<DynOutputStream>, wasi::filesystem::types::ErrorCode>> {
613 unreachable!("no filesystem")
614 }
615 fn append_via_stream(
616 &mut self,
617 _: Resource<wasi::filesystem::types::Descriptor>,
618 ) -> Result<Result<Resource<DynOutputStream>, wasi::filesystem::types::ErrorCode>> {
619 unreachable!("no filesystem")
620 }
621 fn advise(
622 &mut self,
623 _: Resource<wasi::filesystem::types::Descriptor>,
624 _: u64,
625 _: u64,
626 _: wasi::filesystem::types::Advice,
627 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
628 unreachable!("no filesystem")
629 }
630 fn sync_data(
631 &mut self,
632 _: Resource<wasi::filesystem::types::Descriptor>,
633 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
634 unreachable!("no filesystem")
635 }
636 fn get_flags(
637 &mut self,
638 _: Resource<wasi::filesystem::types::Descriptor>,
639 ) -> Result<Result<wasi::filesystem::types::DescriptorFlags, wasi::filesystem::types::ErrorCode>>
640 {
641 unreachable!("no filesystem")
642 }
643 fn get_type(
644 &mut self,
645 _: Resource<wasi::filesystem::types::Descriptor>,
646 ) -> Result<Result<wasi::filesystem::types::DescriptorType, wasi::filesystem::types::ErrorCode>>
647 {
648 unreachable!("no filesystem")
649 }
650 fn set_size(
651 &mut self,
652 _: Resource<wasi::filesystem::types::Descriptor>,
653 _: u64,
654 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
655 unreachable!("no filesystem")
656 }
657 fn set_times(
658 &mut self,
659 _: Resource<wasi::filesystem::types::Descriptor>,
660 _: wasi::filesystem::types::NewTimestamp,
661 _: wasi::filesystem::types::NewTimestamp,
662 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
663 unreachable!("no filesystem")
664 }
665 fn read(
666 &mut self,
667 _: Resource<wasi::filesystem::types::Descriptor>,
668 _: u64,
669 _: u64,
670 ) -> Result<Result<(Vec<u8>, bool), wasi::filesystem::types::ErrorCode>> {
671 unreachable!("no filesystem")
672 }
673 fn write(
674 &mut self,
675 _: Resource<wasi::filesystem::types::Descriptor>,
676 _: Vec<u8>,
677 _: u64,
678 ) -> Result<Result<u64, wasi::filesystem::types::ErrorCode>> {
679 unreachable!("no filesystem")
680 }
681
682 fn read_directory(
683 &mut self,
684 _: Resource<wasi::filesystem::types::Descriptor>,
685 ) -> Result<
686 Result<
687 Resource<wasi::filesystem::types::DirectoryEntryStream>,
688 wasi::filesystem::types::ErrorCode,
689 >,
690 > {
691 unreachable!("no filesystem")
692 }
693 fn sync(
694 &mut self,
695 _: Resource<wasi::filesystem::types::Descriptor>,
696 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
697 unreachable!("no filesystem")
698 }
699 fn create_directory_at(
700 &mut self,
701 _: Resource<wasi::filesystem::types::Descriptor>,
702 _: String,
703 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
704 unreachable!("no filesystem")
705 }
706 fn stat(
707 &mut self,
708 _: Resource<wasi::filesystem::types::Descriptor>,
709 ) -> Result<Result<wasi::filesystem::types::DescriptorStat, wasi::filesystem::types::ErrorCode>>
710 {
711 unreachable!("no filesystem")
712 }
713 fn stat_at(
714 &mut self,
715 _: Resource<wasi::filesystem::types::Descriptor>,
716 _: wasi::filesystem::types::PathFlags,
717 _: String,
718 ) -> Result<Result<wasi::filesystem::types::DescriptorStat, wasi::filesystem::types::ErrorCode>>
719 {
720 unreachable!("no filesystem")
721 }
722 fn set_times_at(
723 &mut self,
724 _: Resource<wasi::filesystem::types::Descriptor>,
725 _: wasi::filesystem::types::PathFlags,
726 _: String,
727 _: wasi::filesystem::types::NewTimestamp,
728 _: wasi::filesystem::types::NewTimestamp,
729 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
730 unreachable!("no filesystem")
731 }
732 fn link_at(
733 &mut self,
734 _: Resource<wasi::filesystem::types::Descriptor>,
735 _: wasi::filesystem::types::PathFlags,
736 _: String,
737 _: Resource<wasi::filesystem::types::Descriptor>,
738 _: String,
739 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
740 unreachable!("no filesystem")
741 }
742 fn open_at(
743 &mut self,
744 _: Resource<wasi::filesystem::types::Descriptor>,
745 _: wasi::filesystem::types::PathFlags,
746 _: String,
747 _: wasi::filesystem::types::OpenFlags,
748 _: wasi::filesystem::types::DescriptorFlags,
749 ) -> Result<
750 Result<Resource<wasi::filesystem::types::Descriptor>, wasi::filesystem::types::ErrorCode>,
751 > {
752 unreachable!("no filesystem")
753 }
754 fn readlink_at(
755 &mut self,
756 _: Resource<wasi::filesystem::types::Descriptor>,
757 _: String,
758 ) -> Result<Result<String, wasi::filesystem::types::ErrorCode>> {
759 unreachable!("no filesystem")
760 }
761 fn remove_directory_at(
762 &mut self,
763 _: Resource<wasi::filesystem::types::Descriptor>,
764 _: String,
765 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
766 unreachable!("no filesystem")
767 }
768 fn rename_at(
769 &mut self,
770 _: Resource<wasi::filesystem::types::Descriptor>,
771 _: String,
772 _: Resource<wasi::filesystem::types::Descriptor>,
773 _: String,
774 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
775 unreachable!("no filesystem")
776 }
777 fn symlink_at(
778 &mut self,
779 _: Resource<wasi::filesystem::types::Descriptor>,
780 _: String,
781 _: String,
782 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
783 unreachable!("no filesystem")
784 }
785 fn unlink_file_at(
786 &mut self,
787 _: Resource<wasi::filesystem::types::Descriptor>,
788 _: String,
789 ) -> Result<Result<(), wasi::filesystem::types::ErrorCode>> {
790 unreachable!("no filesystem")
791 }
792 fn is_same_object(
793 &mut self,
794 _: Resource<wasi::filesystem::types::Descriptor>,
795 _: Resource<wasi::filesystem::types::Descriptor>,
796 ) -> Result<bool> {
797 unreachable!("no filesystem")
798 }
799 fn metadata_hash(
800 &mut self,
801 _: Resource<wasi::filesystem::types::Descriptor>,
802 ) -> Result<
803 Result<wasi::filesystem::types::MetadataHashValue, wasi::filesystem::types::ErrorCode>,
804 > {
805 unreachable!("no filesystem")
806 }
807 fn metadata_hash_at(
808 &mut self,
809 _: Resource<wasi::filesystem::types::Descriptor>,
810 _: wasi::filesystem::types::PathFlags,
811 _: String,
812 ) -> Result<
813 Result<wasi::filesystem::types::MetadataHashValue, wasi::filesystem::types::ErrorCode>,
814 > {
815 unreachable!("no filesystem")
816 }
817
818 fn drop(&mut self, _: Resource<wasi::filesystem::types::Descriptor>) -> Result<()> {
819 unreachable!("no filesystem")
820 }
821}
822impl wasi::filesystem::types::HostDirectoryEntryStream for ExampleCtx {
825 fn read_directory_entry(
826 &mut self,
827 _: Resource<wasi::filesystem::types::DirectoryEntryStream>,
828 ) -> Result<
829 Result<Option<wasi::filesystem::types::DirectoryEntry>, wasi::filesystem::types::ErrorCode>,
830 > {
831 unreachable!("no filesystem")
832 }
833 fn drop(&mut self, _: Resource<wasi::filesystem::types::DirectoryEntryStream>) -> Result<()> {
834 unreachable!("no filesystem")
835 }
836}
837
838impl wasi::filesystem::types::Host for ExampleCtx {
841 fn filesystem_error_code(
842 &mut self,
843 _: Resource<wasmtime_wasi_io::streams::Error>,
844 ) -> Result<Option<wasi::filesystem::types::ErrorCode>> {
845 Ok(None)
846 }
847}