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
110
111
112
113
114
115
116
//! Refs #211: where `forjar dist` writes, resolved once for every generator.
//!
//! Split out of `dist.rs` to keep that file under the 500-line gate.
use std::path::Path;
/// Where `forjar dist` writes, resolved once for every generator.
///
/// `--help` says `-o` is an "Output file (for single artifact) or directory
/// (with --all)". Refs #211 makes that true of both halves:
///
/// * exactly one artifact selected → `-o` is that artifact's path;
/// * more than one (typically `--all`) → `-o` is the directory, i.e. an alias
/// for `--output-dir`, instead of being used as the installer's file name
/// while everything else silently fell back to `./dist`.
#[derive(Debug)]
pub(crate) enum DistOutput {
/// One artifact, written to exactly this path.
Single(std::path::PathBuf),
/// Several artifacts, written under this directory.
Dir(std::path::PathBuf),
}
impl DistOutput {
/// Base directory for artifacts that were not given an explicit path.
pub(crate) fn dir(&self) -> &Path {
match self {
Self::Single(_) => Path::new("dist"),
Self::Dir(d) => d,
}
}
/// The explicit single-artifact path, if one was requested.
pub(crate) fn single_file(&self) -> Option<&Path> {
match self {
Self::Single(p) => Some(p),
Self::Dir(_) => None,
}
}
}
/// Where one artifact goes: the explicit `-o` path, else `<out_dir>/<name>`.
pub(crate) fn artifact_path(
single: Option<&Path>,
out_dir: &Path,
default_name: &str,
) -> std::path::PathBuf {
single
.map(Path::to_path_buf)
.unwrap_or_else(|| out_dir.join(default_name))
}
/// Resolve `-o` / `--output-dir` against the set of selected artifacts.
pub(crate) fn resolve_dist_output(
output: Option<&Path>,
output_dir: Option<&Path>,
selected: &[bool],
) -> Result<DistOutput, String> {
let count = selected.iter().filter(|s| **s).count();
match (output, output_dir) {
(Some(o), Some(d)) => Err(format!(
"--output {} and --output-dir {} both given — pass one",
o.display(),
d.display()
)),
(None, Some(d)) => Ok(DistOutput::Dir(d.to_path_buf())),
(Some(o), None) if count == 1 => Ok(DistOutput::Single(o.to_path_buf())),
// More than one artifact and a single -o: it can only mean a directory.
(Some(o), None) => Ok(DistOutput::Dir(o.to_path_buf())),
(None, None) => Ok(DistOutput::Dir(std::path::PathBuf::from("dist"))),
}
}
pub(crate) struct GeneratedArtifact {
kind: String,
path: String,
size: usize,
}
impl GeneratedArtifact {
pub(crate) fn new(kind: &str, path: &Path, size: usize) -> Self {
Self {
kind: kind.to_string(),
path: path.display().to_string(),
size,
}
}
}
pub(crate) fn print_json(artifacts: &[GeneratedArtifact]) {
let items: Vec<String> = artifacts
.iter()
.map(|a| {
format!(
r#"{{"kind":"{}","path":"{}","size":{}}}"#,
a.kind, a.path, a.size
)
})
.collect();
println!(
r#"{{"artifacts":[{}],"count":{}}}"#,
items.join(","),
artifacts.len()
);
}
pub(crate) fn print_summary(artifacts: &[GeneratedArtifact]) {
println!("Generated {} distribution artifact(s):", artifacts.len());
for a in artifacts {
if a.size > 0 {
println!(" {} → {} ({} bytes)", a.kind, a.path, a.size);
} else {
println!(" {} → {}", a.kind, a.path);
}
}
}