Skip to content

Commit c78d355

Browse files
committed
generate markdown report
1 parent e305908 commit c78d355

File tree

2 files changed

+223
-4
lines changed

2 files changed

+223
-4
lines changed

src/report/markdown.rs

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
use crate::crates::Crate;
2+
use crate::experiments::Experiment;
3+
use crate::prelude::*;
4+
use crate::report::analyzer::{ReportConfig, ReportCrates, ToolchainSelect};
5+
use crate::report::{
6+
crate_to_url, BuildTestResult, Comparison, CrateResult, ReportWriter, ResultName, TestResults,
7+
};
8+
use crate::utils::serialize::to_vec;
9+
use indexmap::{IndexMap, IndexSet};
10+
use std::fmt::Write;
11+
12+
#[derive(Serialize)]
13+
enum ReportCratesMD {
14+
Plain(Vec<CrateResult>),
15+
Complete {
16+
// only string keys are allowed in JSON maps
17+
#[serde(serialize_with = "to_vec")]
18+
res: IndexMap<CrateResult, Vec<CrateResult>>,
19+
#[serde(serialize_with = "to_vec")]
20+
orphans: IndexMap<Crate, Vec<CrateResult>>,
21+
},
22+
}
23+
24+
#[derive(Serialize)]
25+
struct ResultsContext<'a> {
26+
ex: &'a Experiment,
27+
categories: Vec<(Comparison, ReportCratesMD)>,
28+
info: IndexMap<Comparison, u32>,
29+
full: bool,
30+
crates_count: usize,
31+
}
32+
33+
fn write_crate(
34+
mut rendered: &mut String,
35+
krate: &CrateResult,
36+
comparison: Comparison,
37+
is_child: bool,
38+
) -> Fallible<()> {
39+
let get_run_name = |run: &BuildTestResult| {
40+
if !is_child {
41+
run.res.long_name()
42+
} else {
43+
run.res.name()
44+
}
45+
};
46+
47+
let runs = [
48+
krate.runs[0]
49+
.as_ref()
50+
.map(get_run_name)
51+
.unwrap_or_else(|| "unavailable".into()),
52+
krate.runs[0]
53+
.as_ref()
54+
.map(|run| run.log.to_owned())
55+
.unwrap_or_else(|| "#".into()),
56+
krate.runs[1]
57+
.as_ref()
58+
.map(get_run_name)
59+
.unwrap_or_else(|| "unavailable".into()),
60+
krate.runs[1]
61+
.as_ref()
62+
.map(|run| run.log.to_owned())
63+
.unwrap_or_else(|| "#".into()),
64+
];
65+
66+
let prefix = if is_child { " * " } else { "* " };
67+
68+
if let ReportConfig::Complete(toolchain) = comparison.report_config() {
69+
let (conj, run) = match toolchain {
70+
ToolchainSelect::Start => ("from", 0),
71+
ToolchainSelect::End => ("due to", 2),
72+
};
73+
74+
writeln!(
75+
&mut rendered,
76+
"{}[{}]({}) {} {} **{}** [start]({}/log.txt) | [end]({}/log.txt)",
77+
prefix,
78+
krate.name,
79+
krate.url,
80+
comparison.to_string(),
81+
conj,
82+
runs[run],
83+
runs[1],
84+
runs[3]
85+
)?;
86+
} else {
87+
writeln!(
88+
&mut rendered,
89+
"{}[{}]({}) {} [start]({}/log.txt) | [end]({}/log.txt)",
90+
prefix,
91+
krate.name,
92+
krate.url,
93+
comparison.to_string(),
94+
runs[1],
95+
runs[3]
96+
)?;
97+
};
98+
99+
Ok(())
100+
}
101+
102+
fn render_markdown(context: &ResultsContext) -> Fallible<String> {
103+
let mut rendered = String::new();
104+
105+
//add title
106+
writeln!(&mut rendered, "# Crater report for {}\n\n", context.ex.name)?;
107+
108+
for (comparison, results) in context.categories.iter() {
109+
writeln!(&mut rendered, "\n### {}", comparison.to_string())?;
110+
match results {
111+
ReportCratesMD::Plain(crates) => {
112+
for krate in crates {
113+
write_crate(&mut rendered, krate, *comparison, false)?;
114+
}
115+
}
116+
ReportCratesMD::Complete { res, orphans } => {
117+
for (root, deps) in res {
118+
write_crate(&mut rendered, root, *comparison, false)?;
119+
for krate in deps {
120+
write_crate(&mut rendered, krate, *comparison, true)?;
121+
}
122+
}
123+
124+
for (krate, deps) in orphans {
125+
writeln!(
126+
&mut rendered,
127+
"* [{}]({}) (not covered in crater testing)",
128+
krate,
129+
crate_to_url(&krate)?
130+
)?;
131+
for krate in deps {
132+
write_crate(&mut rendered, krate, *comparison, true)?;
133+
}
134+
}
135+
}
136+
}
137+
}
138+
139+
Ok(rendered)
140+
}
141+
142+
fn write_report<W: ReportWriter>(
143+
ex: &Experiment,
144+
crates_count: usize,
145+
res: &TestResults,
146+
full: bool,
147+
to: &str,
148+
dest: &W,
149+
output_templates: bool,
150+
) -> Fallible<()> {
151+
let categories = res
152+
.categories
153+
.iter()
154+
.filter(|(category, _)| full || category.show_in_summary())
155+
.map(|(&category, crates)| (category, crates.to_owned()))
156+
.map(|(category, crates)| match crates {
157+
ReportCrates::Plain(crates) => (
158+
category,
159+
ReportCratesMD::Plain(crates.into_iter().collect::<Vec<_>>()),
160+
),
161+
ReportCrates::Complete { mut tree, results } => {
162+
let res = results
163+
.into_iter()
164+
.flat_map(|(_key, values)| values.into_iter())
165+
.collect::<IndexSet<_>>() // remove duplicates
166+
.into_iter()
167+
.map(|krate| {
168+
// done here to avoid cloning krate
169+
let deps = tree.remove(&krate.krate).unwrap_or_default();
170+
(krate, deps)
171+
})
172+
.collect::<IndexMap<_, _>>();
173+
174+
(category, ReportCratesMD::Complete { res, orphans: tree })
175+
}
176+
})
177+
.collect();
178+
179+
let context = ResultsContext {
180+
ex,
181+
categories,
182+
info: res.info.clone(),
183+
full,
184+
crates_count,
185+
};
186+
187+
let markdown = render_markdown(&context)?;
188+
info!("generating {}", to);
189+
dest.write_string(to, markdown.into(), &mime::TEXT_PLAIN)?;
190+
191+
if output_templates {
192+
dest.write_string(
193+
[to, ".context.json"].concat(),
194+
serde_json::to_string(&context)?.into(),
195+
&mime::TEXT_PLAIN,
196+
)?;
197+
}
198+
199+
Ok(())
200+
}
201+
202+
pub fn write_markdown_report<W: ReportWriter>(
203+
ex: &Experiment,
204+
crates_count: usize,
205+
res: &TestResults,
206+
dest: &W,
207+
output_templates: bool,
208+
) -> Fallible<()> {
209+
write_report(
210+
ex,
211+
crates_count,
212+
res,
213+
false,
214+
"markdown.md",
215+
dest,
216+
output_templates,
217+
)?;
218+
Ok(())
219+
}

src/report/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ pub struct RawTestResults {
4646
pub crates: Vec<CrateResult>,
4747
}
4848

49-
#[cfg_attr(test, derive(Debug, PartialEq))]
50-
#[derive(Serialize, Deserialize, Clone)]
49+
#[cfg_attr(test, derive(Debug))]
50+
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
5151
pub struct CrateResult {
5252
name: String,
5353
url: String,
@@ -107,8 +107,8 @@ impl Comparison {
107107
}
108108
}
109109

110-
#[cfg_attr(test, derive(Debug, PartialEq))]
111-
#[derive(Serialize, Deserialize, Clone)]
110+
#[cfg_attr(test, derive(Debug))]
111+
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
112112
struct BuildTestResult {
113113
res: TestResult,
114114
log: String,

0 commit comments

Comments
 (0)