Skip to content

Commit c6f761c

Browse files
Implement forwards compatibility
1 parent 84ea6b1 commit c6f761c

File tree

3 files changed

+56
-3
lines changed

3 files changed

+56
-3
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ ic_principal = "0.1.1"
1313
notify = "8.2.0"
1414
pocket-ic = { git = "https://github.com/dfinity/ic", rev = "97ad9167816c28cf91ed76fece6c6a04a77d7d89" }
1515
reqwest = { version = "0.12.24", default-features = false, features = ["rustls-tls", "json"] }
16+
semver = "1.0.27"
1617
serde = { version = "1.0.228", features = ["derive"] }
1718
serde_json = "1.0.145"
1819
sysinfo = "0.37.2"

src/main.rs

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
use std::{
22
fs,
33
io::ErrorKind,
4+
mem,
45
net::{IpAddr, SocketAddr},
56
path::PathBuf,
67
time::Duration,
78
};
89

910
use anyhow::Context;
10-
use clap::{ArgAction, Parser, ValueEnum};
11+
use clap::{ArgAction, CommandFactory, Parser, ValueEnum};
1112
use ic_principal::Principal;
1213
use notify::{Event, RecursiveMode, Watcher, recommended_watcher};
1314
use pocket_ic::{
1415
PocketIcBuilder,
1516
common::rest::{AutoProgressConfig, IcpFeatures, IcpFeaturesConfig, InstanceHttpGatewayConfig},
1617
};
1718
use reqwest::Client;
19+
use semver::{Version, VersionReq};
1820
use serde::Serialize;
1921
use sysinfo::{ProcessesToUpdate, Signal, System};
2022
use tempfile::TempDir;
@@ -24,6 +26,8 @@ use tokio::{process::Command, signal::unix::SignalKind};
2426
#[derive(Parser)]
2527
#[command(version)]
2628
struct Cli {
29+
#[arg(long)]
30+
interface_version: Option<Version>,
2731
#[arg(long)]
2832
gateway_port: Option<u16>,
2933
#[arg(long)]
@@ -50,10 +54,12 @@ struct Cli {
5054
stdout_file: Option<PathBuf>,
5155
#[arg(long)]
5256
stderr_file: Option<PathBuf>,
53-
#[arg(long)]
57+
#[arg(long, requires = "interface_version")]
5458
status_dir: Option<PathBuf>,
5559
#[arg(long)]
5660
verbose: bool,
61+
#[arg(trailing_var_arg = true, hide = true, allow_hyphen_values = true)]
62+
unknown_args: Vec<String>,
5763
}
5864

5965
#[derive(ValueEnum, Clone)]
@@ -85,7 +91,9 @@ async fn main() -> anyhow::Result<()> {
8591
stderr_file,
8692
status_dir,
8793
verbose,
88-
} = Cli::parse();
94+
interface_version: _,
95+
unknown_args: _,
96+
} = get_errorchecked_args();
8997
// pocket-ic is expected to be installed next to the launcher (see package.sh)
9098
let pocketic_server_path = if let Some(path) = pocketic_server_path {
9199
path
@@ -298,6 +306,49 @@ async fn main() -> anyhow::Result<()> {
298306
Ok(())
299307
}
300308

309+
fn get_errorchecked_args() -> Cli {
310+
let mut cli = Cli::parse();
311+
let mut command = Cli::command();
312+
let Some(interface_version) = &cli.interface_version else {
313+
if !cli.unknown_args.is_empty() {
314+
unknown_arg(&mut command, &cli.unknown_args[0]);
315+
}
316+
return cli;
317+
};
318+
let our_version = Version::parse("1.0.0").expect("valid version");
319+
let requirement = VersionReq::parse("^1.0.0").expect("valid version req");
320+
if !requirement.matches(interface_version) {
321+
eprintln!(
322+
"Error: Unsupported interface version {interface_version}. Supported versions: {requirement}",
323+
);
324+
std::process::exit(1);
325+
}
326+
if !cli.unknown_args.is_empty() {
327+
if *interface_version == our_version {
328+
unknown_arg(&mut command, &cli.unknown_args[0]);
329+
} else {
330+
let mut unknown_args = vec![];
331+
while !cli.unknown_args.is_empty() {
332+
let mut prev_unknown_args = mem::take(&mut cli.unknown_args);
333+
unknown_args.push(prev_unknown_args.remove(0));
334+
cli.update_from(&prev_unknown_args);
335+
}
336+
eprintln!("Warning: Unknown launcher parameters: {unknown_args:?}");
337+
}
338+
}
339+
cli
340+
}
341+
342+
fn unknown_arg(cmd: &mut clap::Command, arg: &str) -> ! {
343+
let mut err = clap::Error::new(clap::error::ErrorKind::UnknownArgument);
344+
err.insert(
345+
clap::error::ContextKind::InvalidArg,
346+
clap::error::ContextValue::String(arg.to_string()),
347+
);
348+
let err = err.format(cmd);
349+
err.exit();
350+
}
351+
301352
#[derive(Serialize)]
302353
struct Status {
303354
v: String,

0 commit comments

Comments
 (0)