gha_expression_proof/
render.rs1use crate::model::{CheckStatus, EvaluationReceipt};
2use anyhow::Result;
3use clap::ValueEnum;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
6pub enum OutputFormat {
7 Text,
8 Json,
9 Markdown,
10}
11
12pub fn render_receipt(receipt: &EvaluationReceipt, format: OutputFormat) -> Result<String> {
13 match format {
14 OutputFormat::Text => Ok(render_text(receipt)),
15 OutputFormat::Json => Ok(format!("{}\n", serde_json::to_string_pretty(receipt)?)),
16 OutputFormat::Markdown => Ok(render_markdown(receipt)),
17 }
18}
19
20fn render_text(receipt: &EvaluationReceipt) -> String {
21 let mut out = String::new();
22 out.push_str("gha-expression-proof receipt\n");
23 out.push_str(&format!("mode: {}\n", receipt.mode));
24 if let Some(expression) = &receipt.expression {
25 out.push_str(&format!("expression: {expression}\n"));
26 }
27 if let Some(result) = &receipt.result_string {
28 out.push_str(&format!("result: {result}\n"));
29 }
30 if let Some(rendered) = &receipt.rendered {
31 out.push_str(&format!("rendered: {rendered}\n"));
32 }
33 out.push_str(&format!(
34 "summary: {} passed, {} warnings, {} failed, {} skipped\n",
35 receipt.summary.passed,
36 receipt.summary.warnings,
37 receipt.summary.failed,
38 receipt.summary.skipped
39 ));
40
41 out.push_str("\nchecks:\n");
42 for check in &receipt.checks {
43 out.push_str(&format!(
44 "[{}] {} - {}\n",
45 check.status.as_str(),
46 check.id,
47 check.message
48 ));
49 }
50
51 out
52}
53
54fn render_markdown(receipt: &EvaluationReceipt) -> String {
55 let mut out = String::new();
56 out.push_str("# gha-expression-proof Receipt\n\n");
57 out.push_str(&format!("- Mode: `{}`\n", receipt.mode));
58 if let Some(expression) = &receipt.expression {
59 out.push_str(&format!("- Expression: `{}`\n", escape_pipe(expression)));
60 }
61 if let Some(result) = &receipt.result_string {
62 out.push_str(&format!("- Result: `{}`\n", escape_pipe(result)));
63 }
64 if let Some(rendered) = &receipt.rendered {
65 out.push_str(&format!("- Rendered: `{}`\n", escape_pipe(rendered)));
66 }
67 out.push_str(&format!(
68 "- Summary: {} passed, {} warnings, {} failed, {} skipped\n",
69 receipt.summary.passed,
70 receipt.summary.warnings,
71 receipt.summary.failed,
72 receipt.summary.skipped
73 ));
74
75 out.push_str("\n## Checks\n\n");
76 out.push_str("| Status | ID | Message |\n| --- | --- | --- |\n");
77 for check in &receipt.checks {
78 out.push_str(&format!(
79 "| `{}` | `{}` | {} |\n",
80 label(check.status),
81 check.id,
82 escape_pipe(&check.message)
83 ));
84 }
85 out
86}
87
88fn label(status: CheckStatus) -> &'static str {
89 status.as_str()
90}
91
92fn escape_pipe(value: &str) -> String {
93 value.replace('|', "\\|").replace('\n', " ")
94}