1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
//! 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;
mod rsync;
mod run;
mod sh;
mod spec;
use std::io::Read;
use std::process::ExitCode;
/// 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 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)
} else {
std::fs::read_to_string(path)
};
let text = match text {
Ok(text) => text,
Err(e) => {
eprintln!("populate: can't read {path}: {e}");
return ExitCode::from(USAGE);
}
};
let source: spec::Sources = match serde_json::from_str(&text) {
Ok(source) => source,
Err(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(), local) {
Ok(context) => context,
Err(failure) => return exit(failure),
};
let result = populate::populate(&context, &spec);
run::ignore_signals();
context.clean_up();
match result {
Ok(()) => ExitCode::SUCCESS,
Err(failure) => exit(failure),
}
}
/// Says why populate fails, unless a command did, and returns the exit
/// status for that.
fn exit(failure: run::Failure) -> ExitCode {
match failure {
run::Failure::Message(message) => {
eprintln!("{message}");
ExitCode::FAILURE
}
run::Failure::Command(status) => ExitCode::from(status),
run::Failure::Signal(signal) => ExitCode::from(128 + signal as u8),
}
}
|