@@ -6,6 +6,7 @@ use std::{
66 time:: Duration ,
77} ;
88
9+ use anyhow:: Context ;
910use clap:: { ArgAction , Parser , ValueEnum } ;
1011use ic_principal:: Principal ;
1112use notify:: { Event , RecursiveMode , Watcher , recommended_watcher} ;
@@ -67,7 +68,7 @@ enum SubnetKind {
6768}
6869
6970#[ tokio:: main]
70- async fn main ( ) {
71+ async fn main ( ) -> anyhow :: Result < ( ) > {
7172 let Cli {
7273 gateway_port,
7374 config_port,
@@ -90,9 +91,9 @@ async fn main() {
9091 path
9192 } else {
9293 let assumed = std:: env:: current_exe ( )
93- . unwrap ( )
94+ . context ( "Failed to get current exe path" ) ?
9495 . parent ( )
95- . unwrap ( )
96+ . expect ( "exe path should always have parent" )
9697 . join ( "pocket-ic" ) ;
9798 if !assumed. exists ( ) {
9899 eprintln ! (
@@ -103,29 +104,38 @@ async fn main() {
103104 assumed
104105 } ;
105106 // We learn the port by pocket-ic writing it to a file
106- let tmpdir = TempDir :: new ( ) . unwrap ( ) ;
107+ let tmpdir = TempDir :: new ( ) . context ( "failed to create temporary directory" ) ? ;
107108 let port_file = tmpdir. path ( ) . join ( "pocketic.port" ) ;
108109 let ( tx, mut rx) = tokio:: sync:: mpsc:: channel ( 10 ) ;
109110 let mut watcher = recommended_watcher ( {
110111 let port_file = port_file. clone ( ) ;
111112 move |event : Result < Event , notify:: Error > | {
112- event. unwrap ( ) ;
113+ if let Err ( e) = event {
114+ _ = tx. blocking_send ( Err ( e) . context ( "failed to watch directory for port file" ) ) ;
115+ return ;
116+ }
113117 match fs:: read_to_string ( & port_file) {
114118 Ok ( contents) => {
115119 if contents. ends_with ( '\n' ) {
116- let port: u16 = contents. trim ( ) . parse ( ) . unwrap ( ) ;
117- let _ = tx. blocking_send ( port) ;
120+ match contents. trim ( ) . parse :: < u16 > ( ) {
121+ Ok ( port) => _ = tx. blocking_send ( Ok ( port) ) ,
122+ Err ( e) => {
123+ _ = tx. blocking_send (
124+ Err ( e) . context ( "failed to parse port from port file" ) ,
125+ )
126+ }
127+ }
118128 }
119129 }
120130 Err ( e) if e. kind ( ) == ErrorKind :: NotFound => { }
121131 Err ( e) => panic ! ( "Failed to read port file: {}" , e) ,
122132 } ;
123133 }
124134 } )
125- . unwrap ( ) ;
135+ . context ( "failed to create file watcher" ) ? ;
126136 watcher
127137 . watch ( tmpdir. path ( ) , RecursiveMode :: Recursive )
128- . unwrap ( ) ;
138+ . context ( "failed to watch temporary directory" ) ? ;
129139 // pocket-ic CLI setup begins here
130140 let mut cmd = Command :: new ( & pocketic_server_path) ;
131141 // the default TTL is 1m - increase to 30 days. We manually shut the network down instead of relying on idle timeout.
@@ -138,11 +148,11 @@ async fn main() {
138148 cmd. arg ( "--ip-addr" ) . arg ( bind. to_string ( ) ) ;
139149 }
140150 if let Some ( stdout_file) = stdout_file {
141- let file = std:: fs:: File :: create ( stdout_file) . unwrap ( ) ;
151+ let file = std:: fs:: File :: create ( stdout_file) . context ( "failed to create stdout file" ) ? ;
142152 cmd. stdout ( file) ;
143153 }
144154 if let Some ( stderr_file) = stderr_file {
145- let file = std:: fs:: File :: create ( stderr_file) . unwrap ( ) ;
155+ let file = std:: fs:: File :: create ( stderr_file) . context ( "failed to create stderr file" ) ? ;
146156 cmd. stderr ( file) ;
147157 }
148158 if !verbose {
@@ -152,13 +162,22 @@ async fn main() {
152162 {
153163 cmd. process_group ( 0 ) ;
154164 }
155- let mut child = cmd. spawn ( ) . unwrap ( ) ;
156- let config_port = rx. recv ( ) . await . unwrap ( ) ;
165+ let mut child = cmd
166+ . spawn ( )
167+ . context ( "failed to spawn pocket-ic server process" ) ?;
168+ let config_port = rx
169+ . recv ( )
170+ . await
171+ . expect ( "failed to receive port from watcher" ) ?;
157172 drop ( watcher) ;
158173 // pocket-ic CLI setup ends here
159174 // initial HTTP setup
160175 let mut pic = PocketIcBuilder :: new ( )
161- . with_server_url ( format ! ( "http://127.0.0.1:{config_port}/" ) . parse ( ) . unwrap ( ) )
176+ . with_server_url (
177+ format ! ( "http://127.0.0.1:{config_port}/" )
178+ . parse ( )
179+ . expect ( "valid url" ) ,
180+ )
162181 . with_http_gateway ( InstanceHttpGatewayConfig {
163182 ip_addr : bind. map ( |ip| ip. to_string ( ) ) ,
164183 port : gateway_port,
@@ -213,44 +232,58 @@ async fn main() {
213232 let progress_url = pic
214233 . get_server_url ( )
215234 . join ( & format ! ( "/instances/{}/auto_progress" , pic. instance_id) )
216- . unwrap ( ) ;
235+ . expect ( "valid url" ) ;
217236 client
218237 . post ( progress_url)
219238 . json ( & AutoProgressConfig {
220239 artificial_delay_ms,
221240 } )
222241 . send ( )
223242 . await
224- . unwrap ( )
243+ . context ( "failed to send auto progress config to pocket-ic" ) ?
225244 . error_for_status ( )
226- . unwrap ( ) ;
245+ . context ( "failed to configure pocket-ic for auto-progress" ) ? ;
227246 let topology = pic. topology ( ) . await ;
228247 let default_ecid = Principal :: from_slice ( & topology. default_effective_canister_id . canister_id ) ;
229- let gateway_url = pic. url ( ) . unwrap ( ) ;
248+ let gateway_url = pic. url ( ) . expect ( "gateway url set in builder" ) ;
230249 // write everything to the status file
231250 if let Some ( status_dir) = status_dir {
232251 let status_file = status_dir. join ( "status.json" ) ;
233252 let status = Status {
234253 v : "1" . to_string ( ) ,
235254 instance_id : pic. instance_id ,
236255 config_port,
237- gateway_port : gateway_url. port ( ) . unwrap ( ) ,
238- root_key : hex:: encode ( pic. root_key ( ) . await . unwrap ( ) ) ,
256+ gateway_port : gateway_url
257+ . port_or_known_default ( )
258+ . expect ( "gateway urls should have a known port" ) ,
259+ root_key : hex:: encode (
260+ pic. root_key ( )
261+ . await
262+ . expect ( "root key should be available if there is a root subnet" ) ,
263+ ) ,
239264 default_effective_canister_id : default_ecid,
240265 } ;
241- let mut contents = serde_json:: to_string ( & status) . unwrap ( ) ;
266+ let mut contents = serde_json:: to_string ( & status) . expect ( "infallible serialization" ) ;
242267 contents. push ( '\n' ) ;
243268 println ! ( "launcher: writing status to {}" , status_file. display( ) ) ;
244- fs:: write ( status_file, contents) . unwrap ( ) ;
269+ fs:: write ( status_file, contents) . context ( "failed to write status file" ) ? ;
245270 }
246271 let ctrlc = tokio:: signal:: ctrl_c ( ) ;
247- let mut sigterm = tokio:: signal:: unix:: signal ( SignalKind :: terminate ( ) ) . unwrap ( ) ;
248- select ! {
249- _ = ctrlc => { } ,
250- _ = sigterm. recv( ) => { } ,
272+ #[ cfg( unix) ]
273+ {
274+ let mut sigterm = tokio:: signal:: unix:: signal ( SignalKind :: terminate ( ) )
275+ . context ( "failed to install SIGTERM handler" ) ?;
276+ select ! {
277+ res = ctrlc => res. context( "failed to listen for ctrl-c" ) ?,
278+ _ = sigterm. recv( ) => { } ,
279+ }
280+ }
281+ #[ cfg( not( unix) ) ]
282+ {
283+ ctrlc. await . context ( "failed to listen for ctrl-c" ) ?;
251284 }
252285 pic. drop ( ) . await ;
253- let pid = child. id ( ) . unwrap ( ) as usize ;
286+ let pid = child. id ( ) . expect ( "child process should have an id" ) as usize ;
254287 let mut sys = System :: new ( ) ;
255288 sys. refresh_processes ( ProcessesToUpdate :: Some ( & [ pid. into ( ) ] ) , true ) ;
256289 if let Some ( process) = sys. process ( pid. into ( ) ) {
@@ -262,6 +295,7 @@ async fn main() {
262295 let _ = child. kill( ) . await ;
263296 }
264297 }
298+ Ok ( ( ) )
265299}
266300
267301#[ derive( Serialize ) ]
0 commit comments