1- #![ allow( unused) ] // TODO: remove
1+ use anyhow:: { anyhow, Context as _} ;
2+ use tokio:: io:: { AsyncRead , AsyncReadExt as _, AsyncWrite , AsyncWriteExt as _} ;
3+ use wasmtime:: component:: { stream, Accessor , AccessorTask , Resource , StreamReader , StreamWriter } ;
24
3- use anyhow:: Context as _;
4- use wasmtime:: component:: Resource ;
5-
6- use crate :: p3:: bindings:: cli:: terminal_input:: TerminalInput ;
7- use crate :: p3:: bindings:: cli:: terminal_output:: TerminalOutput ;
85use crate :: p3:: bindings:: cli:: {
96 environment, exit, stderr, stdin, stdout, terminal_input, terminal_output, terminal_stderr,
107 terminal_stdin, terminal_stdout,
118} ;
12- use crate :: p3:: cli:: { WasiCliImpl , WasiCliView } ;
13- use crate :: p3:: ResourceView as _;
9+ use crate :: p3:: cli:: { I32Exit , TerminalInput , TerminalOutput , WasiCliImpl , WasiCliView } ;
10+ use crate :: p3:: { next_item, ResourceView as _} ;
11+
12+ struct InputTask < T > {
13+ input : T ,
14+ tx : StreamWriter < u8 > ,
15+ }
16+
17+ impl < T , U , V > AccessorTask < T , U , wasmtime:: Result < ( ) > > for InputTask < V >
18+ where
19+ V : AsyncRead + Send + Sync + Unpin + ' static ,
20+ {
21+ async fn run ( mut self , store : & mut Accessor < T , U > ) -> wasmtime:: Result < ( ) > {
22+ let mut tx = self . tx ;
23+ loop {
24+ let mut buf = vec ! [ 0 ; 8096 ] ;
25+ match self . input . read ( & mut buf) . await {
26+ Ok ( 0 ) => {
27+ store. with ( |mut view| tx. close ( & mut view) . context ( "failed to close stream" ) ) ?;
28+ return Ok ( ( ) ) ;
29+ }
30+ Ok ( n) => {
31+ buf. truncate ( n) ;
32+ let fut =
33+ store. with ( |view| tx. write ( view, buf) . context ( "failed to send chunk" ) ) ?;
34+ let Some ( tail) = fut. into_future ( ) . await else {
35+ break Ok ( ( ) ) ;
36+ } ;
37+ tx = tail;
38+ }
39+ Err ( _err) => {
40+ // TODO: Close the stream with the real error context
41+ store. with ( |mut view| {
42+ tx. close_with_error ( & mut view, 0 )
43+ . context ( "failed to close stream" )
44+ } ) ?;
45+ return Ok ( ( ) ) ;
46+ }
47+ }
48+ }
49+ }
50+ }
51+
52+ struct OutputTask < T > {
53+ output : T ,
54+ data : StreamReader < u8 > ,
55+ }
56+
57+ impl < T , U , V > AccessorTask < T , U , wasmtime:: Result < ( ) > > for OutputTask < V >
58+ where
59+ V : AsyncWrite + Send + Sync + Unpin + ' static ,
60+ {
61+ async fn run ( mut self , store : & mut Accessor < T , U > ) -> wasmtime:: Result < ( ) > {
62+ let fut = store. with ( |mut view| {
63+ self . data
64+ . read ( & mut view)
65+ . context ( "failed to read from stream" )
66+ } ) ?;
67+ let mut fut = fut. into_future ( ) ;
68+ ' outer: loop {
69+ let Some ( ( tail, buf) ) = fut. await else {
70+ return Ok ( ( ) ) ;
71+ } ;
72+ let mut buf = buf. as_slice ( ) ;
73+ loop {
74+ match self . output . write ( & buf) . await {
75+ Ok ( n) => {
76+ if n == buf. len ( ) {
77+ fut = next_item ( store, tail) ?;
78+ continue ' outer;
79+ } else {
80+ buf = & buf[ n..] ;
81+ }
82+ }
83+ Err ( _err) => {
84+ // TODO: Report the error to the guest
85+ return store. with ( |mut view| {
86+ tail. close ( & mut view) . context ( "failed to close stream" )
87+ } ) ;
88+ }
89+ }
90+ }
91+ }
92+ }
93+ }
1494
1595impl < T > terminal_input:: Host for WasiCliImpl < T > where T : WasiCliView { }
1696impl < T > terminal_output:: Host for WasiCliImpl < T > where T : WasiCliView { }
@@ -44,7 +124,15 @@ where
44124 T : WasiCliView ,
45125{
46126 fn get_terminal_stdin ( & mut self ) -> wasmtime:: Result < Option < Resource < TerminalInput > > > {
47- todo ! ( )
127+ if self . cli ( ) . stdin . is_terminal ( ) {
128+ let fd = self
129+ . table ( )
130+ . push ( TerminalInput )
131+ . context ( "failed to push terminal resource to table" ) ?;
132+ Ok ( Some ( fd) )
133+ } else {
134+ Ok ( None )
135+ }
48136 }
49137}
50138
@@ -53,7 +141,15 @@ where
53141 T : WasiCliView ,
54142{
55143 fn get_terminal_stdout ( & mut self ) -> wasmtime:: Result < Option < Resource < TerminalOutput > > > {
56- todo ! ( )
144+ if self . cli ( ) . stdout . is_terminal ( ) {
145+ let fd = self
146+ . table ( )
147+ . push ( TerminalOutput )
148+ . context ( "failed to push terminal resource to table" ) ?;
149+ Ok ( Some ( fd) )
150+ } else {
151+ Ok ( None )
152+ }
57153 }
58154}
59155
@@ -62,34 +158,69 @@ where
62158 T : WasiCliView ,
63159{
64160 fn get_terminal_stderr ( & mut self ) -> wasmtime:: Result < Option < Resource < TerminalOutput > > > {
65- todo ! ( )
161+ if self . cli ( ) . stderr . is_terminal ( ) {
162+ let fd = self
163+ . table ( )
164+ . push ( TerminalOutput )
165+ . context ( "failed to push terminal resource to table" ) ?;
166+ Ok ( Some ( fd) )
167+ } else {
168+ Ok ( None )
169+ }
66170 }
67171}
68172
69173impl < T > stdin:: Host for WasiCliImpl < T >
70174where
71175 T : WasiCliView ,
72176{
73- fn get_stdin ( & mut self ) -> wasmtime:: Result < wasmtime:: component:: StreamReader < u8 > > {
74- todo ! ( )
177+ async fn get_stdin < U : ' static > (
178+ store : & mut Accessor < U , Self > ,
179+ ) -> wasmtime:: Result < StreamReader < u8 > > {
180+ store. with ( |mut view| {
181+ let ( tx, rx) = stream ( & mut view) . context ( "failed to create stream" ) ?;
182+ let stdin = view. cli ( ) . stdin . reader ( ) ;
183+ view. spawn ( InputTask { input : stdin, tx } ) ;
184+ Ok ( rx)
185+ } )
75186 }
76187}
77188
78189impl < T > stdout:: Host for WasiCliImpl < T >
79190where
80191 T : WasiCliView ,
81192{
82- fn set_stdout ( & mut self , data : wasmtime:: component:: StreamReader < u8 > ) -> wasmtime:: Result < ( ) > {
83- todo ! ( )
193+ async fn set_stdout < U : ' static > (
194+ store : & mut Accessor < U , Self > ,
195+ data : StreamReader < u8 > ,
196+ ) -> wasmtime:: Result < ( ) > {
197+ store. with ( |mut view| {
198+ let stdout = view. cli ( ) . stdout . writer ( ) ;
199+ view. spawn ( OutputTask {
200+ output : stdout,
201+ data,
202+ } ) ;
203+ Ok ( ( ) )
204+ } )
84205 }
85206}
86207
87208impl < T > stderr:: Host for WasiCliImpl < T >
88209where
89210 T : WasiCliView ,
90211{
91- fn set_stderr ( & mut self , data : wasmtime:: component:: StreamReader < u8 > ) -> wasmtime:: Result < ( ) > {
92- todo ! ( )
212+ async fn set_stderr < U : ' static > (
213+ store : & mut Accessor < U , Self > ,
214+ data : StreamReader < u8 > ,
215+ ) -> wasmtime:: Result < ( ) > {
216+ store. with ( |mut view| {
217+ let stderr = view. cli ( ) . stderr . writer ( ) ;
218+ view. spawn ( OutputTask {
219+ output : stderr,
220+ data,
221+ } ) ;
222+ Ok ( ( ) )
223+ } )
93224 }
94225}
95226
@@ -115,10 +246,14 @@ where
115246 T : WasiCliView ,
116247{
117248 fn exit ( & mut self , status : Result < ( ) , ( ) > ) -> wasmtime:: Result < ( ) > {
118- todo ! ( )
249+ let status = match status {
250+ Ok ( ( ) ) => 0 ,
251+ Err ( ( ) ) => 1 ,
252+ } ;
253+ Err ( anyhow ! ( I32Exit ( status) ) )
119254 }
120255
121256 fn exit_with_code ( & mut self , status_code : u8 ) -> wasmtime:: Result < ( ) > {
122- todo ! ( )
257+ Err ( anyhow ! ( I32Exit ( status_code . into ( ) ) ) )
123258 }
124259}
0 commit comments