fuchsia_triage/
result_format.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::act::ActionResults;
6use std::fmt;
7
8pub struct ActionResultFormatter<'a> {
9    action_results: &'a ActionResults,
10}
11
12impl fmt::Display for ActionResultFormatter<'_> {
13    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14        self.write_text(f)
15    }
16}
17
18impl ActionResultFormatter<'_> {
19    pub fn new(action_results: &ActionResults) -> ActionResultFormatter<'_> {
20        ActionResultFormatter { action_results }
21    }
22
23    fn write_text(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        Self::write_section(f, "Errors", &self.action_results.errors, true)?;
25        Self::write_section(
26            f,
27            "Featured Values",
28            &self.action_results.gauges,
29            self.action_results.sort_gauges,
30        )?;
31        Self::write_section(f, "Warnings", &self.action_results.warnings, true)?;
32        self.write_plugins(f)?;
33        if self.action_results.verbose {
34            Self::write_section(
35                f,
36                "Featured Values (Not Computable)",
37                &self.action_results.broken_gauges,
38                true,
39            )?;
40            Self::write_section(f, "Info", &self.action_results.infos, true)?;
41        }
42        Ok(())
43    }
44
45    fn write_section(
46        f: &mut fmt::Formatter<'_>,
47        label: &str,
48        lines: &Vec<String>,
49        sort: bool,
50    ) -> fmt::Result {
51        if lines.is_empty() {
52            return Ok(());
53        }
54
55        Self::write_underlined(f, label)?;
56        let mut sorted_lines;
57        let output_lines = if sort {
58            sorted_lines = lines.clone();
59            sorted_lines.sort();
60            &sorted_lines
61        } else {
62            lines
63        };
64        for line in output_lines {
65            writeln!(f, "{line}")?;
66        }
67        writeln!(f)
68    }
69
70    fn write_underlined(f: &mut fmt::Formatter<'_>, content: &str) -> fmt::Result {
71        writeln!(f, "{content}")?;
72        writeln!(f, "{}", "-".repeat(content.len()))
73    }
74
75    fn write_plugins(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        for (name, result) in &self.action_results.sub_results {
77            if !self.action_results.verbose
78                && result.gauges.is_empty()
79                && result.errors.is_empty()
80                && result.warnings.is_empty()
81            {
82                writeln!(f, "Plugin '{name}' - nothing to show\n")?;
83                continue;
84            }
85            writeln!(f, "Plugin '{name}'")?;
86            // TODO(https://fxbug.dev/42076794): use self.verbose flag correctly in plugins.
87            ActionResultFormatter::new(result).write_text(f)?;
88        }
89        Ok(())
90    }
91}
92
93#[cfg(test)]
94mod test {
95    use super::*;
96
97    #[fuchsia::test]
98    fn action_result_formatter_to_warnings_when_no_actions_triggered() {
99        let action_results = ActionResults::new();
100        let formatter = ActionResultFormatter::new(&action_results);
101
102        assert_eq!(String::from(""), formatter.to_string());
103    }
104
105    #[fuchsia::test]
106    fn action_result_formatter_to_text_when_actions_triggered() {
107        let warnings = String::from(
108            "\
109Warnings
110--------
111w1
112w2
113
114",
115        );
116
117        let mut action_results = ActionResults::new();
118        action_results.warnings.push(String::from("w1"));
119        action_results.warnings.push(String::from("w2"));
120
121        let formatter = ActionResultFormatter::new(&action_results);
122
123        assert_eq!(warnings, formatter.to_string());
124    }
125
126    #[fuchsia::test]
127    fn action_result_formatter_to_text_with_gauges() {
128        let warnings = String::from(
129            "\
130Featured Values
131---------------
132g1
133
134Warnings
135--------
136w1
137w2
138
139",
140        );
141
142        let mut action_results = ActionResults::new();
143        action_results.warnings.push(String::from("w1"));
144        action_results.warnings.push(String::from("w2"));
145        action_results.gauges.push(String::from("g1"));
146
147        let formatter = ActionResultFormatter::new(&action_results);
148
149        assert_eq!(warnings, formatter.to_string());
150    }
151
152    #[fuchsia::test]
153    fn action_result_formatter_sorts_output() {
154        let warnings = String::from(
155            "\
156Featured Values
157---------------
158g1
159g2
160
161Warnings
162--------
163w1
164w2
165
166Plugin 'Crashes' - nothing to show
167
168Plugin 'Warning'
169Warnings
170--------
171w1
172w2
173
174Plugin 'Gauges'
175Featured Values
176---------------
177g2
178g1
179
180",
181        );
182
183        {
184            let mut action_results = ActionResults::new();
185            action_results.warnings.push(String::from("w1"));
186            action_results.warnings.push(String::from("w2"));
187            action_results.gauges.push(String::from("g1"));
188            action_results.gauges.push(String::from("g2"));
189            let mut warnings_plugin = ActionResults::new();
190            warnings_plugin.warnings.push(String::from("w1"));
191            warnings_plugin.warnings.push(String::from("w2"));
192            let mut gauges_plugin = ActionResults::new();
193            gauges_plugin.gauges.push(String::from("g2"));
194            gauges_plugin.gauges.push(String::from("g1"));
195            gauges_plugin.sort_gauges = false;
196            action_results
197                .sub_results
198                .push(("Crashes".to_string(), Box::new(ActionResults::new())));
199            action_results.sub_results.push(("Warning".to_string(), Box::new(warnings_plugin)));
200            action_results.sub_results.push(("Gauges".to_string(), Box::new(gauges_plugin)));
201
202            let formatter = ActionResultFormatter::new(&action_results);
203
204            assert_eq!(warnings, formatter.to_string());
205        }
206
207        // Same as before, but reversed to test sorting.
208        {
209            let mut action_results = ActionResults::new();
210            action_results.warnings.push(String::from("w2"));
211            action_results.warnings.push(String::from("w1"));
212            action_results.gauges.push(String::from("g2"));
213            action_results.gauges.push(String::from("g1"));
214            let mut warnings_plugin = ActionResults::new();
215            warnings_plugin.warnings.push(String::from("w2"));
216            warnings_plugin.warnings.push(String::from("w1"));
217            let mut gauges_plugin = ActionResults::new();
218            gauges_plugin.gauges.push(String::from("g2"));
219            gauges_plugin.gauges.push(String::from("g1"));
220            gauges_plugin.sort_gauges = false;
221            action_results
222                .sub_results
223                .push(("Crashes".to_string(), Box::new(ActionResults::new())));
224            action_results.sub_results.push(("Warning".to_string(), Box::new(warnings_plugin)));
225            action_results.sub_results.push(("Gauges".to_string(), Box::new(gauges_plugin)));
226
227            let formatter = ActionResultFormatter::new(&action_results);
228
229            assert_eq!(warnings, formatter.to_string());
230        }
231    }
232
233    #[fuchsia::test]
234    fn action_result_verbose_works() {
235        let readable_warnings = String::from(
236            "\
237Featured Values
238---------------
239g1: 42
240
241Warnings
242--------
243w1
244
245",
246        );
247        let verbose_warnings = format!(
248            "{}{}",
249            readable_warnings,
250            "\
251Featured Values (Not Computable)
252--------------------------------
253g2: N/A
254
255Info
256----
257i1
258
259"
260        );
261        let mut action_results = ActionResults::new();
262        action_results.warnings.push(String::from("w1"));
263        action_results.gauges.push(String::from("g1: 42"));
264        action_results.infos.push(String::from("i1"));
265        action_results.broken_gauges.push(String::from("g2: N/A"));
266        let mut verbose_action_results = action_results.clone();
267        verbose_action_results.verbose = true;
268        assert_eq!(readable_warnings, ActionResultFormatter::new(&action_results).to_string());
269        assert_eq!(
270            verbose_warnings,
271            ActionResultFormatter::new(&verbose_action_results).to_string()
272        );
273    }
274}