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
//! en: CLI progress sink: turns [`Event`]s into stderr output per --progress
//! (docs/cli.ja.md §3.5). `ndjson` writes one JSON event per line; `bar`/default prints
//! coarse phase lines; `none` is silent. Retries are always surfaced.
//! ja: CLI の進捗シンク。--progress に応じて [`Event`] を stderr へ出す。
use std::io::Write;
use ch32rv_contract::event::Event;
use ch32rv_contract::progress::ProgressSink;
use crate::args::{Cli, ProgressMode};
pub struct CliSink {
mode: ProgressMode,
}
/// Pick the sink for this invocation. JSON result mode implies quiet phase output.
pub fn sink(cli: &Cli) -> CliSink {
let mode = cli.progress.unwrap_or_else(|| {
// Default: ndjson when --json (machine), otherwise bar on a tty, else none.
if cli.json {
ProgressMode::None
} else if std::io::IsTerminal::is_terminal(&std::io::stderr()) {
ProgressMode::Bar
} else {
ProgressMode::None
}
});
CliSink { mode }
}
impl ProgressSink for CliSink {
fn event(&self, ev: &Event) {
match self.mode {
ProgressMode::None => {
// Even when silent, a retry is worth a line (docs/cli.ja.md §3.5).
if let Event::Retry {
phase,
attempt,
cause,
} = ev
{
eprintln!("retry: {phase} attempt {attempt} ({cause})");
}
}
ProgressMode::Ndjson => {
if let Ok(line) = serde_json::to_string(ev) {
let mut err = std::io::stderr().lock();
let _ = writeln!(err, "{line}");
}
}
ProgressMode::Bar => match ev {
Event::Phase { name, .. } => eprintln!("{name}..."),
Event::Retry {
phase,
attempt,
cause,
} => {
eprintln!("retry: {phase} attempt {attempt} ({cause})")
}
Event::Warn { code, msg } => eprintln!("warning[{code}]: {msg}"),
_ => {}
},
}
}
}