Skip to content

Commit 84ea6b1

Browse files
chore: replace most panics with anyhow (#5)
1 parent 01aa88a commit 84ea6b1

File tree

3 files changed

+113
-32
lines changed

3 files changed

+113
-32
lines changed

Cargo.lock

Lines changed: 50 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,13 @@ authors = ["DFINITY Stiftung <sdk@dfinity.org>"]
66
license = "Apache-2.0"
77

88
[dependencies]
9+
anyhow = { version = "1.0.100", features = ["backtrace"] }
910
clap = { version = "4.5.53", features = ["derive"] }
1011
hex = "0.4.3"
1112
ic_principal = "0.1.1"
1213
notify = "8.2.0"
1314
pocket-ic = { git = "https://github.com/dfinity/ic", rev = "97ad9167816c28cf91ed76fece6c6a04a77d7d89" }
14-
reqwest = { version = "0.12.24", default-features = false, features = [
15-
"rustls-tls",
16-
"json",
17-
] }
15+
reqwest = { version = "0.12.24", default-features = false, features = ["rustls-tls", "json"] }
1816
serde = { version = "1.0.228", features = ["derive"] }
1917
serde_json = "1.0.145"
2018
sysinfo = "0.37.2"

src/main.rs

Lines changed: 61 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::{
66
time::Duration,
77
};
88

9+
use anyhow::Context;
910
use clap::{ArgAction, Parser, ValueEnum};
1011
use ic_principal::Principal;
1112
use 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

Comments
 (0)