@@ -3,11 +3,13 @@ use std::{
3
3
process:: Command ,
4
4
} ;
5
5
6
- use anyhow:: Result ;
6
+ use anyhow:: { Context , Result } ;
7
7
8
8
/// Helpers intended for [`std::process::Command`].
9
9
pub ( crate ) trait CommandRunExt {
10
10
fn run ( & mut self ) -> Result < ( ) > ;
11
+ /// Execute the child process, parsing its stdout as JSON.
12
+ fn run_and_parse_json < T : serde:: de:: DeserializeOwned > ( & mut self ) -> Result < T > ;
11
13
}
12
14
13
15
/// Helpers intended for [`std::process::ExitStatus`].
@@ -68,6 +70,15 @@ impl CommandRunExt for Command {
68
70
self . stderr ( stderr. try_clone ( ) ?) ;
69
71
self . status ( ) ?. check_status ( stderr)
70
72
}
73
+
74
+ fn run_and_parse_json < T : serde:: de:: DeserializeOwned > ( & mut self ) -> Result < T > {
75
+ let mut stdout = tempfile:: tempfile ( ) ?;
76
+ self . stdout ( stdout. try_clone ( ) ?) ;
77
+ self . run ( ) ?;
78
+ stdout. seek ( std:: io:: SeekFrom :: Start ( 0 ) ) . context ( "seek" ) ?;
79
+ let stdout = std:: io:: BufReader :: new ( stdout) ;
80
+ serde_json:: from_reader ( stdout) . map_err ( Into :: into)
81
+ }
71
82
}
72
83
73
84
/// Helpers intended for [`tokio::process::Command`].
@@ -118,6 +129,21 @@ fn command_run_ext() {
118
129
) ;
119
130
}
120
131
132
+ #[ test]
133
+ fn command_run_ext_json ( ) {
134
+ #[ derive( serde:: Deserialize ) ]
135
+ struct Foo {
136
+ a : String ,
137
+ b : u32 ,
138
+ }
139
+ let v: Foo = Command :: new ( "echo" )
140
+ . arg ( r##"{"a": "somevalue", "b": 42}"## )
141
+ . run_and_parse_json ( )
142
+ . unwrap ( ) ;
143
+ assert_eq ! ( v. a, "somevalue" ) ;
144
+ assert_eq ! ( v. b, 42 ) ;
145
+ }
146
+
121
147
#[ tokio:: test]
122
148
async fn async_command_run_ext ( ) {
123
149
use tokio:: process:: Command as AsyncCommand ;
0 commit comments