|  | 
|  | 1 | +use crate::io::{self, BorrowedBuf, BorrowedCursor}; | 
|  | 2 | +use wasip2::cli; | 
|  | 3 | +use wasip2::io::streams::{Error, InputStream, OutputStream, StreamError}; | 
|  | 4 | + | 
|  | 5 | +pub struct Stdin(Option<InputStream>); | 
|  | 6 | +pub struct Stdout(Option<OutputStream>); | 
|  | 7 | +pub struct Stderr(Option<OutputStream>); | 
|  | 8 | + | 
|  | 9 | +fn error_to_io(err: Error) -> io::Error { | 
|  | 10 | +    // There exists a function in `wasi:filesystem` to optionally acquire an | 
|  | 11 | +    // error code from an error, but the streams in use in this module are | 
|  | 12 | +    // exclusively used with stdio meaning that a filesystem error is not | 
|  | 13 | +    // possible here. | 
|  | 14 | +    // | 
|  | 15 | +    // In lieu of an error code, which WASIp2 does not specify, this instead | 
|  | 16 | +    // carries along the `to_debug_string` implementation that the host | 
|  | 17 | +    // supplies. If this becomes too expensive in the future this could also | 
|  | 18 | +    // become `io::Error::from_raw_os_error(libc::EIO)` or similar. | 
|  | 19 | +    io::Error::new(io::ErrorKind::Other, err.to_debug_string()) | 
|  | 20 | +} | 
|  | 21 | + | 
|  | 22 | +impl Stdin { | 
|  | 23 | +    pub const fn new() -> Stdin { | 
|  | 24 | +        Stdin(None) | 
|  | 25 | +    } | 
|  | 26 | + | 
|  | 27 | +    fn stream(&mut self) -> &InputStream { | 
|  | 28 | +        self.0.get_or_insert_with(cli::stdin::get_stdin) | 
|  | 29 | +    } | 
|  | 30 | +} | 
|  | 31 | + | 
|  | 32 | +impl io::Read for Stdin { | 
|  | 33 | +    fn read(&mut self, data: &mut [u8]) -> io::Result<usize> { | 
|  | 34 | +        let mut buf = BorrowedBuf::from(data); | 
|  | 35 | +        self.read_buf(buf.unfilled())?; | 
|  | 36 | +        Ok(buf.len()) | 
|  | 37 | +    } | 
|  | 38 | + | 
|  | 39 | +    fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { | 
|  | 40 | +        match self.stream().blocking_read(u64::try_from(buf.capacity()).unwrap()) { | 
|  | 41 | +            Ok(result) => { | 
|  | 42 | +                buf.append(&result); | 
|  | 43 | +                Ok(()) | 
|  | 44 | +            } | 
|  | 45 | +            Err(StreamError::Closed) => Ok(()), | 
|  | 46 | +            Err(StreamError::LastOperationFailed(e)) => Err(error_to_io(e)), | 
|  | 47 | +        } | 
|  | 48 | +    } | 
|  | 49 | +} | 
|  | 50 | + | 
|  | 51 | +impl Stdout { | 
|  | 52 | +    pub const fn new() -> Stdout { | 
|  | 53 | +        Stdout(None) | 
|  | 54 | +    } | 
|  | 55 | + | 
|  | 56 | +    fn stream(&mut self) -> &OutputStream { | 
|  | 57 | +        self.0.get_or_insert_with(cli::stdout::get_stdout) | 
|  | 58 | +    } | 
|  | 59 | +} | 
|  | 60 | + | 
|  | 61 | +fn write(stream: &OutputStream, buf: &[u8]) -> io::Result<usize> { | 
|  | 62 | +    // WASIp2's `blocking_write_and_flush` function is defined as accepting no | 
|  | 63 | +    // more than 4096 bytes. Larger writes can be issued by manually using | 
|  | 64 | +    // `check_write`, `write`, and `blocking_flush`, but for now just go ahead | 
|  | 65 | +    // and use `blocking_write_and_flush` and report a short write and let a | 
|  | 66 | +    // higher level loop over the result. | 
|  | 67 | +    const MAX: usize = 4096; | 
|  | 68 | +    let buf = &buf[..buf.len().min(MAX)]; | 
|  | 69 | +    match stream.blocking_write_and_flush(buf) { | 
|  | 70 | +        Ok(()) => Ok(buf.len()), | 
|  | 71 | +        Err(StreamError::Closed) => Ok(0), | 
|  | 72 | +        Err(StreamError::LastOperationFailed(e)) => Err(error_to_io(e)), | 
|  | 73 | +    } | 
|  | 74 | +} | 
|  | 75 | + | 
|  | 76 | +impl io::Write for Stdout { | 
|  | 77 | +    fn write(&mut self, data: &[u8]) -> io::Result<usize> { | 
|  | 78 | +        write(self.stream(), data) | 
|  | 79 | +    } | 
|  | 80 | + | 
|  | 81 | +    fn flush(&mut self) -> io::Result<()> { | 
|  | 82 | +        // Note that `OutputStream` has a `flush` function but for stdio all | 
|  | 83 | +        // writes are accompanied with a flush which means that this flush | 
|  | 84 | +        // doesn't need to do anything. | 
|  | 85 | +        Ok(()) | 
|  | 86 | +    } | 
|  | 87 | +} | 
|  | 88 | + | 
|  | 89 | +impl Stderr { | 
|  | 90 | +    pub const fn new() -> Stderr { | 
|  | 91 | +        Stderr(None) | 
|  | 92 | +    } | 
|  | 93 | + | 
|  | 94 | +    fn stream(&mut self) -> &OutputStream { | 
|  | 95 | +        self.0.get_or_insert_with(cli::stderr::get_stderr) | 
|  | 96 | +    } | 
|  | 97 | +} | 
|  | 98 | + | 
|  | 99 | +impl io::Write for Stderr { | 
|  | 100 | +    fn write(&mut self, data: &[u8]) -> io::Result<usize> { | 
|  | 101 | +        write(self.stream(), data) | 
|  | 102 | +    } | 
|  | 103 | + | 
|  | 104 | +    fn flush(&mut self) -> io::Result<()> { | 
|  | 105 | +        // See `Stdout::flush` for why this is a noop. | 
|  | 106 | +        Ok(()) | 
|  | 107 | +    } | 
|  | 108 | +} | 
|  | 109 | + | 
|  | 110 | +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; | 
|  | 111 | + | 
|  | 112 | +pub fn is_ebadf(_err: &io::Error) -> bool { | 
|  | 113 | +    // WASIp2 stdio streams are always available so ebadf never shows up. | 
|  | 114 | +    false | 
|  | 115 | +} | 
|  | 116 | + | 
|  | 117 | +pub fn panic_output() -> Option<impl io::Write> { | 
|  | 118 | +    Some(Stderr::new()) | 
|  | 119 | +} | 
0 commit comments