Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/examples/min-platform/embedding/src/wasi.rs
3076 views
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
844