1use crate::act::ActionContext;
6use crate::act_structured::StructuredActionContext;
7use crate::metrics::MetricState;
8use crate::metrics::metric_value::MetricValue;
9use anyhow::{Error, bail};
10use injectable_time::{MonotonicInstant, TimeSource};
11use regex::Regex;
12
13pub(crate) mod act; pub(crate) mod act_structured; pub(crate) mod config; pub mod inspect_logger;
17pub(crate) mod metrics; pub(crate) mod plugins; pub(crate) mod result_format; pub(crate) mod validate; pub use act::{Action, ActionResults, SnapshotTrigger, WarningVec};
23pub use act_structured::TriageOutput;
24pub use config::{ActionTagDirective, DataFetcher, DiagnosticData, ParseResult, Source};
25pub use result_format::ActionResultFormatter;
26
27const DEVICE_UPTIME_KEY: &str = "device.uptime";
28
29fn time_from_snapshot(files: &[DiagnosticData]) -> Option<i64> {
30 if let Some(file) = files.iter().find(|file| file.source == Source::Annotations)
31 && let DataFetcher::KeyValue(fetcher) = &file.data
32 && let MetricValue::String(duration) = fetcher.fetch(DEVICE_UPTIME_KEY)
33 {
34 let re = Regex::new(r"^(\d+)d(\d+)h(\d+)m(\d+)s$").unwrap();
35 if let Some(c) = re.captures(&duration) {
36 let dhms = (c.get(1), c.get(2), c.get(3), c.get(4));
37 if let (Some(d), Some(h), Some(m), Some(s)) = dhms {
38 let dhms = (
39 d.as_str().parse::<i64>(),
40 h.as_str().parse::<i64>(),
41 m.as_str().parse::<i64>(),
42 s.as_str().parse::<i64>(),
43 );
44 if let (Ok(d), Ok(h), Ok(m), Ok(s)) = dhms {
45 return Some(1_000_000_000 * (s + 60 * (m + 60 * (h + 24 * d))));
46 }
47 }
48 }
49 }
50 None
51}
52
53pub fn analyze(
57 diagnostic_data: &[DiagnosticData],
58 parse_result: &ParseResult,
59) -> Result<ActionResults, Error> {
60 inner_analyze(diagnostic_data, parse_result, false )
61}
62
63pub fn analyze_verbose(
67 diagnostic_data: &[DiagnosticData],
68 parse_result: &ParseResult,
69) -> Result<ActionResults, Error> {
70 inner_analyze(diagnostic_data, parse_result, true )
71}
72
73fn inner_analyze(
74 diagnostic_data: &[DiagnosticData],
75 parse_result: &ParseResult,
76 verbose: bool,
77) -> Result<ActionResults, Error> {
78 parse_result.reset_state();
79 let now = time_from_snapshot(diagnostic_data);
80 let mut action_context =
81 ActionContext::new(&parse_result.metrics, &parse_result.actions, diagnostic_data, now);
82 action_context.set_verbose(verbose);
83 Ok(action_context.process().clone())
84}
85
86pub fn analyze_structured(
89 diagnostic_data: &[DiagnosticData],
90 parse_result: &ParseResult,
91) -> Result<TriageOutput, Error> {
92 parse_result.reset_state();
93 let now = time_from_snapshot(diagnostic_data);
94 let mut structured_action_context = StructuredActionContext::new(
95 &parse_result.metrics,
96 &parse_result.actions,
97 diagnostic_data,
98 now,
99 );
100 Ok(structured_action_context.process().clone())
101}
102
103pub fn snapshots(
105 data: &[DiagnosticData],
106 parse_result: &ParseResult,
107) -> (Vec<SnapshotTrigger>, act::WarningVec) {
108 parse_result.reset_state();
109 let now = Some(MonotonicInstant::new().now());
110 let evaluator = ActionContext::new(&parse_result.metrics, &parse_result.actions, data, now);
111 evaluator.into_snapshots()
112}
113
114pub fn all_selectors(parse: &ParseResult) -> Vec<String> {
115 parse.all_selectors()
116}
117
118pub fn evaluate_int_math(expression: &str) -> Result<i64, Error> {
119 metric_value_to_int(MetricState::evaluate_math(expression))
120}
121
122pub fn metric_value_to_int(metric_value: MetricValue) -> Result<i64, Error> {
123 match metric_value {
124 MetricValue::Int(i) => Ok(i),
125 MetricValue::Float(f) => match metrics::safe_float_to_int(f) {
126 Some(i) => Ok(i),
127 None => bail!("Non-numeric float result {}", f),
128 },
129 MetricValue::Problem(problem) => bail!("Eval error: {:?}", problem),
130 bad_type => bail!("Non-numeric result: {:?}", bad_type),
131 }
132}
133
134#[cfg(test)]
135mod test {
136 use super::*;
137
138 #[fuchsia::test]
139 fn time_parses_correctly() {
140 fn file(name: &str, source: Source, contents: &str) -> DiagnosticData {
141 DiagnosticData::new(name.to_string(), source, contents.to_string()).unwrap()
142 }
143 assert_eq!(time_from_snapshot(&[]), None);
144 let files = vec![file("foo.json", Source::Annotations, r#"{"a":"b"}"#)];
146 assert_eq!(time_from_snapshot(&files), None);
147 let files = vec![file("any.name.works", Source::Annotations, r#"{"device.uptime":"b"}"#)];
148 assert_eq!(time_from_snapshot(&files), None);
149 let files = vec![file("foo.json", Source::Annotations, r#"{"device.uptime":"1h1m1s"}"#)];
150 assert_eq!(time_from_snapshot(&files), None);
151 let files = vec![file("foo.json", Source::Annotations, r#"{"device.uptime":"1d1h1m"}"#)];
152 assert_eq!(time_from_snapshot(&files), None);
153 let files = vec![file("foo.json", Source::Annotations, r#"{"device.uptime":"0d0h0m0s"}"#)];
154 assert_eq!(time_from_snapshot(&files), Some(0));
155 let files = vec![file("a.b", Source::Annotations, r#"{"device.uptime":"2d3h4m5s"}"#)];
156 assert_eq!(time_from_snapshot(&files), Some(1_000_000_000 * 183845));
157 let files = vec![file("a.b", Source::Annotations, r#"{"device.uptime":"11d13h17m19s"}"#)];
158 let seconds = 19 + 17 * 60 + 13 * 3600 + 11 * 3600 * 24;
159 assert_eq!(time_from_snapshot(&files), Some(1_000_000_000 * seconds));
160 let files = vec![file("", Source::Annotations, r#"{"device.uptime":"3d5h7m11s"}"#)];
161 let hours = 5 + 24 * 3;
162 let minutes = 7 + 60 * hours;
163 let seconds = 11 + 60 * minutes;
164 assert_eq!(time_from_snapshot(&files), Some(1_000_000_000 * seconds));
165 let files = vec![file("foo.json", Source::Annotations, r#"{"device.uptime":"0d0h0m1s"}"#)];
166 assert_eq!(time_from_snapshot(&files), Some(1_000_000_000));
167 let files = vec![file("foo.json", Source::Annotations, r#"{"device.uptime":"0d0h1m0s"}"#)];
168 assert_eq!(time_from_snapshot(&files), Some(1_000_000_000 * 60));
169 let files = vec![file("foo.json", Source::Annotations, r#"{"device.uptime":"0d1h0m0s"}"#)];
170 assert_eq!(time_from_snapshot(&files), Some(1_000_000_000 * 60 * 60));
171 let files = vec![file("foo.json", Source::Annotations, r#"{"device.uptime":"1d0h0m0s"}"#)];
172 assert_eq!(time_from_snapshot(&files), Some(1_000_000_000 * 60 * 60 * 24));
173 }
174}