///|
/// Parsed command-line arguments. `CliArgs::parse()` returns `None`
/// when help was printed or the user supplied a malformed `--relay`
/// (the argparse / URL error is already logged — `main` should just
/// return).
struct CliArgs {
relay_url : @url.Url
child_argv : Array[String]
}
///|
/// True for hosts that should use plaintext `ws://` / `http://`
/// instead of TLS. Mirrors `isLocalHost` in
/// `android/.../TerminalScreen.kt` so both clients classify the same
/// host identically.
fn is_local_host(host : String) -> Bool {
host.has_prefix("127.") ||
host.has_prefix("10.") ||
host.has_prefix("192.168.") ||
host.has_prefix("localhost")
}
///|
/// Build the relay WebSocket URL from a bare host (optionally with
/// `:port`). Picks `ws://` for local-style hosts and `wss://`
/// otherwise, appends `/server`, and parses the result.
fn relay_url_from_host(host : String) -> @url.Url raise {
let scheme = if is_local_host(host) { "ws" } else { "wss" }
@url.Url::parse("\{scheme}://\{host}/server")
}
///|
fn build_command() -> @argparse.Command {
@argparse.Command::new(
"tun-server",
about="TUN server that connects to a relay via WebSocket",
options=[
@argparse.OptionArg::new(
"relay",
short='r',
long="relay",
about="Relay host (optionally with :port, e.g. 127.0.0.1:8080)",
default_values=["tun-poc.moonbitlang.cn"],
),
],
positionals=[
@argparse.PositionArg::new(
"child",
num_args=@argparse.ValueRange::new(lower=0),
allow_hyphen_values=true,
about="Program to spawn (defaults to $SHELL or /bin/sh on Unix, %COMSPEC% or cmd.exe on Windows). Use `--` to pass flags to the program.",
),
],
)
}
///|
/// Parse argv. Logs + returns None on argparse error or malformed
/// `--relay` URL; otherwise returns the parsed arguments with the
/// default-shell fallback already applied.
fn CliArgs::parse() -> CliArgs? {
let cmd = build_command()
let matches = cmd.parse() catch {
err => {
@log.error(err.to_string())
println(cmd.render_help())
return None
}
}
let relay_host = matches.values["relay"][0]
let relay_url = relay_url_from_host(relay_host) catch {
e => {
@log.error("invalid --relay host '\{relay_host}': \{e}")
return None
}
}
let child_argv : Array[String] = match matches.values.get("child") {
Some(args) if args.length() > 0 => args
_ =>
if @platform.win32 {
[@env.get_env_var("COMSPEC").unwrap_or("cmd.exe")]
} else {
[@env.get_env_var("SHELL").unwrap_or("/bin/sh")]
}
}
Some({ relay_url, child_argv })
}