11use std:: {
22 fs,
33 io:: ErrorKind ,
4+ mem,
45 net:: { IpAddr , SocketAddr } ,
56 path:: PathBuf ,
67 time:: Duration ,
78} ;
89
910use anyhow:: Context ;
10- use clap:: { ArgAction , Parser , ValueEnum } ;
11+ use clap:: { ArgAction , CommandFactory , Parser , ValueEnum } ;
1112use ic_principal:: Principal ;
1213use notify:: { Event , RecursiveMode , Watcher , recommended_watcher} ;
1314use pocket_ic:: {
1415 PocketIcBuilder ,
1516 common:: rest:: { AutoProgressConfig , IcpFeatures , IcpFeaturesConfig , InstanceHttpGatewayConfig } ,
1617} ;
1718use reqwest:: Client ;
19+ use semver:: { Version , VersionReq } ;
1820use serde:: Serialize ;
1921use sysinfo:: { ProcessesToUpdate , Signal , System } ;
2022use tempfile:: TempDir ;
@@ -24,6 +26,8 @@ use tokio::{process::Command, signal::unix::SignalKind};
2426#[ derive( Parser ) ]
2527#[ command( version) ]
2628struct 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 ) ]
302353struct Status {
303354 v : String ,
0 commit comments