Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions crates/mergify-config/src/simulate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,16 +119,14 @@ pub async fn run(opts: SimulateOptions<'_>, output: &mut dyn Output) -> Result<(
}

fn emit_result(output: &mut dyn Output, response: &SimulatorResponse) -> std::io::Result<()> {
let title = response.title.clone();
let summary = response.summary.clone();
output.emit(&(), &mut |w: &mut dyn Write| {
writeln!(w, "{title}")?;
writeln!(w, "{title}", title = response.title)?;
writeln!(w)?;
// Intentional drift from Python: we print raw Markdown
// instead of rich-rendering it. Machine-readable output is
// still locked; human rendering is flexible per the compat
// contract.
writeln!(w, "{summary}")
writeln!(w, "{summary}", summary = response.summary)
})
}

Expand Down
22 changes: 11 additions & 11 deletions crates/mergify-config/src/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,23 +129,23 @@ fn emit_result(
config_path: &Path,
errors: &[ValidationError],
) -> std::io::Result<()> {
let path_display = config_path.display().to_string();
let errors_copy: Vec<(String, String)> = errors
.iter()
.map(|e| (e.path.clone(), e.message.clone()))
.collect();
output.emit(&(), &mut |w: &mut dyn Write| {
if errors_copy.is_empty() {
let path_display = config_path.display();
if errors.is_empty() {
writeln!(w, "Configuration file '{path_display}' is valid.")?;
} else {
writeln!(
w,
"configuration file '{}' has {} error(s):",
path_display,
errors_copy.len(),
"configuration file '{path_display}' has {n} error(s):",
n = errors.len(),
)?;
for (path, message) in &errors_copy {
writeln!(w, " - {path}: {message}")?;
for err in errors {
writeln!(
w,
" - {path}: {message}",
path = err.path,
message = err.message,
)?;
}
}
Ok(())
Expand Down
17 changes: 17 additions & 0 deletions crates/mergify-core/src/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,23 @@ pub trait Output {
/// no-op to preserve stdout purity; in human mode it writes to
/// stderr so piping stdout into a file is unaffected.
fn status(&mut self, message: &str) -> io::Result<()>;

/// Emit a raw [`serde_json::Value`] as the command's only output.
///
/// Convenience wrapper for the `--json` passthrough commands
/// (`queue status`, `queue show`, `freeze list`, …) — every such
/// command needs the same shape: the value goes out as one JSON
/// document, both when the [`OutputMode`] is `Json` and (via the
/// human closure) when the user passed `--json` but the
/// [`OutputMode`] is still `Human`. Implemented in terms of
/// [`Output::emit`].
fn emit_json_value(&mut self, value: &serde_json::Value) -> io::Result<()> {
self.emit(value, &mut |w| {
let rendered = serde_json::to_string_pretty(value)
.map_err(|err| io::Error::other(err.to_string()))?;
writeln!(w, "{rendered}")
})
}
}

/// `dyn Serialize` cannot be constructed directly because
Expand Down
10 changes: 1 addition & 9 deletions crates/mergify-freeze/src/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub async fn run(opts: ListOptions<'_>, output: &mut dyn Output) -> Result<(), C
.unwrap_or_else(|| serde_json::Value::Array(Vec::new()));

if opts.output_json {
emit_json(output, &freezes)?;
output.emit_json_value(&freezes)?;
return Ok(());
}

Expand All @@ -69,14 +69,6 @@ pub async fn run(opts: ListOptions<'_>, output: &mut dyn Output) -> Result<(), C
Ok(())
}

fn emit_json(output: &mut dyn Output, value: &serde_json::Value) -> std::io::Result<()> {
output.emit(value, &mut |w: &mut dyn Write| {
let rendered = serde_json::to_string_pretty(value)
.map_err(|e| std::io::Error::other(e.to_string()))?;
writeln!(w, "{rendered}")
})
}

fn emit_human(
output: &mut dyn Output,
freezes: &[ScheduledFreeze],
Expand Down
6 changes: 2 additions & 4 deletions crates/mergify-queue/src/pause.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,14 +141,12 @@ fn confirm(skip: bool, is_tty: bool, repository: &str) -> Result<(), CliError> {
}

fn emit_confirmation(output: &mut dyn Output, response: &PauseResponse) -> std::io::Result<()> {
let reason = response.reason.clone();
let paused_at = response.paused_at.clone();
output.emit(&(), &mut |w: &mut dyn Write| {
match &reason {
match &response.reason {
Some(r) => write!(w, "Queue paused: \"{r}\"")?,
None => write!(w, "Queue paused")?,
}
if let Some(ts) = &paused_at {
if let Some(ts) = &response.paused_at {
write!(w, " (since {ts})")?;
}
writeln!(w)
Expand Down
42 changes: 13 additions & 29 deletions crates/mergify-queue/src/show.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ pub async fn run(opts: ShowOptions<'_>, output: &mut dyn Output) -> Result<(), C
};

if opts.output_json {
emit_json(output, &raw)?;
output.emit_json_value(&raw)?;
return Ok(());
}

Expand All @@ -139,14 +139,6 @@ pub async fn run(opts: ShowOptions<'_>, output: &mut dyn Output) -> Result<(), C
Ok(())
}

fn emit_json(output: &mut dyn Output, value: &serde_json::Value) -> std::io::Result<()> {
output.emit(value, &mut |w: &mut dyn Write| {
let rendered = serde_json::to_string_pretty(value)
.map_err(|e| std::io::Error::other(e.to_string()))?;
writeln!(w, "{rendered}")
})
}

fn emit_human(output: &mut dyn Output, view: &PullView, verbose: bool) -> std::io::Result<()> {
let now = Utc::now();
let theme = Theme::detect();
Expand Down Expand Up @@ -310,22 +302,22 @@ fn print_checks_summary(w: &mut dyn Write, theme: &Theme, checks: &[Check]) -> s
write!(
w,
"{S}{passed} passed{R}",
S = enabled_fg(theme, AnsiColor::Green),
S = theme.fg(AnsiColor::Green),
R = theme.reset,
)?;
if pending > 0 {
write!(
w,
", {S}{pending} pending{R}",
S = enabled_fg(theme, AnsiColor::Blue),
S = theme.fg(AnsiColor::Blue),
R = theme.reset,
)?;
}
if failed > 0 {
write!(
w,
", {S}{failed} failed{R}",
S = enabled_fg(theme, AnsiColor::Red),
S = theme.fg(AnsiColor::Red),
R = theme.reset,
)?;
}
Expand Down Expand Up @@ -356,23 +348,15 @@ fn print_checks_summary(w: &mut dyn Write, theme: &Theme, checks: &[Check]) -> s
/// the renderer never crashes on a new API code.
fn check_state_glyph(theme: &Theme, state: &str) -> (&'static str, Style) {
match state {
"success" => ("✓", enabled_fg(theme, AnsiColor::Green)),
"pending" => ("◌", enabled_fg(theme, AnsiColor::Yellow)),
"failure" | "error" | "action_required" => ("✗", enabled_fg(theme, AnsiColor::Red)),
"timed_out" => ("⏰", enabled_fg(theme, AnsiColor::Red)),
"success" => ("✓", theme.fg(AnsiColor::Green)),
"pending" => ("◌", theme.fg(AnsiColor::Yellow)),
"failure" | "error" | "action_required" => ("✗", theme.fg(AnsiColor::Red)),
"timed_out" => ("⏰", theme.fg(AnsiColor::Red)),
"cancelled" | "neutral" | "skipped" | "stale" => ("○", theme.dim),
_ => ("?", theme.dim),
}
}

fn enabled_fg(theme: &Theme, color: AnsiColor) -> Style {
if theme.enabled {
theme.fg(color)
} else {
Style::new()
}
}

fn print_conditions_section(
w: &mut dyn Write,
theme: &Theme,
Expand All @@ -394,9 +378,9 @@ fn print_conditions_section(
let met = top.iter().filter(|s| s.r#match).count();
let total = top.len();
let style = if met == total {
enabled_fg(theme, AnsiColor::Green)
theme.fg(AnsiColor::Green)
} else {
enabled_fg(theme, AnsiColor::Yellow)
theme.fg(AnsiColor::Yellow)
};
writeln!(
w,
Expand All @@ -417,7 +401,7 @@ fn print_conditions_section(
writeln!(
w,
" {S}✗{R} {summary}",
S = enabled_fg(theme, AnsiColor::Red),
S = theme.fg(AnsiColor::Red),
R = theme.reset,
)?;
}
Expand Down Expand Up @@ -466,9 +450,9 @@ fn write_condition_tree(
for (i, node) in nodes.iter().enumerate() {
let (branch, continuation) = tree::branch_chars(i == last);
let (icon, style) = if node.r#match {
("✓", enabled_fg(theme, AnsiColor::Green))
("✓", theme.fg(AnsiColor::Green))
} else {
("✗", enabled_fg(theme, AnsiColor::Red))
("✗", theme.fg(AnsiColor::Red))
};
writeln!(
w,
Expand Down
10 changes: 1 addition & 9 deletions crates/mergify-queue/src/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ pub async fn run(opts: StatusOptions<'_>, output: &mut dyn Output) -> Result<(),
let raw: serde_json::Value = client.get(&path).await?;

if opts.output_json {
emit_json(output, &raw)?;
output.emit_json_value(&raw)?;
} else {
let view: StatusView = serde_json::from_value(raw)
.map_err(|e| CliError::Generic(format!("decode merge queue status response: {e}")))?;
Expand All @@ -159,14 +159,6 @@ fn build_path(repository: &str, branch: Option<&str>) -> String {
path
}

fn emit_json(output: &mut dyn Output, value: &serde_json::Value) -> std::io::Result<()> {
output.emit(value, &mut |w: &mut dyn Write| {
let rendered = serde_json::to_string_pretty(value)
.map_err(|e| std::io::Error::other(e.to_string()))?;
writeln!(w, "{rendered}")
})
}

fn emit_human(output: &mut dyn Output, repository: &str, view: &StatusView) -> std::io::Result<()> {
let now = Utc::now();
let theme = Theme::detect();
Expand Down
Loading