|
| 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 | +} |
0 commit comments