1use fidl_fuchsia_diagnostics as fdiagnostics;
6use std::borrow::Cow;
7use std::fmt::Debug;
8
9#[derive(Debug, Eq, PartialEq)]
10pub enum Segment<'a> {
11 ExactMatch(Cow<'a, str>),
12 Pattern(Cow<'a, str>),
13}
14
15fn contains_unescaped(s: &str, unescaped_char: char) -> bool {
16 let mut iter = s.chars();
17 while let Some(c) = iter.next() {
18 match c {
19 c2 if c2 == unescaped_char => return true,
20 '\\' => {
21 let _ = iter.next();
23 }
24 _ => {}
25 }
26 }
27 false
28}
29
30impl<'a> From<&'a str> for Segment<'a> {
31 fn from(s: &'a str) -> Segment<'a> {
32 if contains_unescaped(s, '*') {
33 return Segment::Pattern(Cow::Owned(unescape(s, &['*'])));
34 }
35 if !s.contains('\\') {
36 return Segment::ExactMatch(Cow::from(s));
37 }
38 Segment::ExactMatch(Cow::Owned(unescape(s, &[])))
39 }
40}
41
42#[derive(Debug, Eq, PartialEq)]
43pub enum TreeNames<'a> {
44 Some(Vec<Cow<'a, str>>),
45 All,
46}
47
48impl<'a> From<Vec<&'a str>> for TreeNames<'a> {
49 fn from(vec: Vec<&'a str>) -> TreeNames<'a> {
50 let mut payload = vec![];
51 for name in vec {
52 if name.contains('\\') {
53 payload.push(Cow::Owned(unescape(name, &[])));
54 } else {
55 payload.push(Cow::Borrowed(name));
56 }
57 }
58 TreeNames::Some(payload)
59 }
60}
61
62fn unescape(value: &str, except: &[char]) -> String {
65 let mut result = String::with_capacity(value.len());
66 let mut iter = value.chars();
67 while let Some(c) = iter.next() {
68 match c {
69 '\\' => {
70 if let Some(c) = iter.next() {
72 if except.contains(&c) {
73 result.push('\\')
74 }
75 result.push(c);
76 }
77 }
78 c => result.push(c),
79 }
80 }
81 result
82}
83
84#[derive(Debug, Eq, PartialEq)]
85pub struct TreeSelector<'a> {
86 pub node: Vec<Segment<'a>>,
87 pub property: Option<Segment<'a>>,
88 pub tree_names: Option<TreeNames<'a>>,
89}
90
91#[derive(Debug, Eq, PartialEq)]
92pub struct ComponentSelector<'a> {
93 pub segments: Vec<Segment<'a>>,
94}
95
96#[derive(Debug, Eq, PartialEq)]
97pub struct Selector<'a> {
98 pub component: ComponentSelector<'a>,
99 pub tree: TreeSelector<'a>,
100}
101
102impl From<Selector<'_>> for fdiagnostics::Selector {
103 fn from(mut selector: Selector<'_>) -> fdiagnostics::Selector {
104 let tree_names = selector.tree.tree_names.take();
105 fdiagnostics::Selector {
106 component_selector: Some(selector.component.into()),
107 tree_selector: Some(selector.tree.into()),
108 tree_names: tree_names.map(|names| names.into()),
109 ..Default::default()
110 }
111 }
112}
113
114impl From<ComponentSelector<'_>> for fdiagnostics::ComponentSelector {
115 fn from(component_selector: ComponentSelector<'_>) -> fdiagnostics::ComponentSelector {
116 fdiagnostics::ComponentSelector {
117 moniker_segments: Some(
118 component_selector.segments.into_iter().map(|segment| segment.into()).collect(),
119 ),
120 ..Default::default()
121 }
122 }
123}
124
125impl From<TreeSelector<'_>> for fdiagnostics::TreeSelector {
126 fn from(tree_selector: TreeSelector<'_>) -> fdiagnostics::TreeSelector {
127 let node_path = tree_selector.node.into_iter().map(|s| s.into()).collect();
128 match tree_selector.property {
129 None => fdiagnostics::TreeSelector::SubtreeSelector(fdiagnostics::SubtreeSelector {
130 node_path,
131 }),
132 Some(property) => {
133 fdiagnostics::TreeSelector::PropertySelector(fdiagnostics::PropertySelector {
134 node_path,
135 target_properties: property.into(),
136 })
137 }
138 }
139 }
140}
141
142impl From<TreeNames<'_>> for fdiagnostics::TreeNames {
143 fn from(tree_names: TreeNames<'_>) -> fdiagnostics::TreeNames {
144 match tree_names {
145 TreeNames::All => fdiagnostics::TreeNames::All(fdiagnostics::All {}),
146 TreeNames::Some(names) => {
147 fdiagnostics::TreeNames::Some(names.iter().map(|n| n.to_string()).collect())
148 }
149 }
150 }
151}
152
153impl From<Segment<'_>> for fdiagnostics::StringSelector {
154 fn from(segment: Segment<'_>) -> fdiagnostics::StringSelector {
155 match segment {
156 Segment::ExactMatch(s) => fdiagnostics::StringSelector::ExactMatch(s.into_owned()),
157 Segment::Pattern(s) => fdiagnostics::StringSelector::StringPattern(s.into_owned()),
158 }
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[fuchsia::test]
167 fn convert_string_to_segment() {
168 assert_eq!(Segment::ExactMatch(Cow::Borrowed("abc")), "abc".into());
169 assert_eq!(Segment::Pattern("a*c".into()), "a*c".into());
170 assert_eq!(Segment::ExactMatch(Cow::Owned("ac*".into())), "ac\\*".into());
171 assert_eq!(Segment::Pattern("a\\*c*".into()), "a\\*c*".into());
172 }
173}