diff options
| author | tv <tv@krebsco.de> | 2026-11-14 00:24:45 +0000 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-14 01:43:19 +0100 |
| commit | 8f08426ae3a2e5f0ecc5d010bc6469b41e3d7678 (patch) | |
| tree | f6573ff55af0b9eacf77e7d58d3668c1129a589a /pkgs/populate/src/main.rs | |
| parent | 57d47a36129b459f0fac519a460c581870a54bbf (diff) | |
populate: a command line instead of a spec
The program takes populate's arguments on its command line, like other
programs: populate [OPTION]... SOURCES [TARGET], with the sources as
JSON in the file SOURCES, or on standard input for -. TARGET has the
syntax of krops' lib's mkTarget, [USER@][HOST][:PORT][PATH], parsed the
same way, and what it leaves out gets filled in when populate runs, as
mkTarget would, from LOGNAME and the machine's name. Options set what
the syntax can't express, or override its parts: --sudo, --ssh-arg,
--user, --host, --port, --path, and --local or --remote for whether the
target is the machine populate runs on, which populate otherwise
decides like isLocalTarget. The sources carry no version.
pkgs.populate passes a target attribute set, as mkTarget returns it,
part by part, with --local or --remote as evaluation decides, so
nothing changes for krops. A target string goes to the program as it
is.
The program also refuses target hosts and users starting with -, which
ssh would take for options, now that a command line can give them.
Diffstat (limited to 'pkgs/populate/src/main.rs')
| -rw-r--r-- | pkgs/populate/src/main.rs | 65 |
1 files changed, 45 insertions, 20 deletions
diff --git a/pkgs/populate/src/main.rs b/pkgs/populate/src/main.rs index 2884fd0..eeed1ef 100644 --- a/pkgs/populate/src/main.rs +++ b/pkgs/populate/src/main.rs @@ -1,7 +1,9 @@ -//! populate SPEC: delivers sources to a target directory, as the spec, a -//! JSON file or - for standard input, says. See docs/design.md. +//! populate [OPTION]... SOURCES [TARGET]: delivers the sources in the JSON +//! file SOURCES, or standard input for -, to the target directory TARGET. +//! See README.md. mod check; +mod cli; mod flake; mod flake_input; mod populate; @@ -13,15 +15,36 @@ mod spec; use std::io::Read; use std::process::ExitCode; -/// Exit status when the spec can't be read, isn't JSON, or isn't a spec. +/// Exit status when the command line is wrong, or the sources can't be read +/// or aren't sources. const USAGE: u8 = 2; fn main() -> ExitCode { let args: Vec<String> = std::env::args().skip(1).collect(); - let [path] = args.as_slice() else { - eprintln!("usage: populate SPEC"); - return ExitCode::from(USAGE); + let options = match cli::parse(&args) { + Ok(cli::Command::Help) => { + print!("{}", cli::USAGE); + return ExitCode::SUCCESS; + } + Ok(cli::Command::Version) => { + println!("populate {}", env!("CARGO_PKG_VERSION")); + return ExitCode::SUCCESS; + } + Ok(cli::Command::Populate(options)) => options, + Err(message) => { + eprintln!("populate: {message}"); + eprint!( + "{}", + cli::USAGE + .lines() + .next() + .map(|l| format!("{l}\n")) + .unwrap_or_default() + ); + return ExitCode::from(USAGE); + } }; + let path = &options.sources; let text = if path == "-" { let mut text = String::new(); std::io::stdin().read_to_string(&mut text).map(|_| text) @@ -35,29 +58,31 @@ fn main() -> ExitCode { return ExitCode::from(USAGE); } }; - let version = serde_json::from_str::<serde_json::Value>(&text) - .ok() - .and_then(|value| value.get("version").and_then(|v| v.as_u64())); - if let Some(version) = version.filter(|v| *v != spec::VERSION) { - eprintln!( - "populate: {path} is a spec of version {version}, this populate reads version {}", - spec::VERSION - ); - return ExitCode::from(USAGE); - } - let spec: spec::Spec = match serde_json::from_str(&text) { - Ok(spec) => spec, + let source: spec::Sources = match serde_json::from_str(&text) { + Ok(source) => source, Err(e) => { - eprintln!("populate: {path} isn't a spec: {e}"); + eprintln!("populate: {path} doesn't contain sources: {e}"); return ExitCode::from(USAGE); } }; + let origin = run::Origin::from_env(); + let target = options + .target + .complete(&origin, options.sudo, options.ssh_args); + let local = options.local.unwrap_or_else(|| origin.is_local(&target)); + let spec = spec::Spec { + backup: options.backup, + force: options.force, + prune: options.prune, + source, + target, + }; if let Err(message) = check::spec(&spec) { eprintln!("{message}"); return ExitCode::FAILURE; } run::trap_signals(); - let context = match run::Context::new(spec.target.clone(), spec.target.local) { + let context = match run::Context::new(spec.target.clone(), local) { Ok(context) => context, Err(failure) => return exit(failure), }; |
