use fidl_fuchsia_diagnostics as fdiagnostics;
use std::borrow::Cow;
use std::fmt::Debug;
#[derive(Debug, Eq, PartialEq)]
pub enum Segment<'a> {
ExactMatch(Cow<'a, str>),
Pattern(Cow<'a, str>),
}
fn contains_unescaped(s: &str, unescaped_char: char) -> bool {
let mut iter = s.chars();
while let Some(c) = iter.next() {
match c {
c2 if c2 == unescaped_char => return true,
'\\' => {
let _ = iter.next();
}
_ => {}
}
}
false
}
impl<'a> From<&'a str> for Segment<'a> {
fn from(s: &'a str) -> Segment<'a> {
if contains_unescaped(s, '*') {
return Segment::Pattern(Cow::Owned(unescape(s, &['*'])));
}
if !s.contains('\\') {
return Segment::ExactMatch(Cow::from(s));
}
Segment::ExactMatch(Cow::Owned(unescape(s, &[])))
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum TreeNames<'a> {
Some(Vec<Cow<'a, str>>),
All,
}
impl<'a> From<Vec<&'a str>> for TreeNames<'a> {
fn from(vec: Vec<&'a str>) -> TreeNames<'a> {
let mut payload = vec![];
for name in vec {
if name.contains('\\') {
payload.push(Cow::Owned(unescape(name, &[])));
} else {
payload.push(Cow::Borrowed(name));
}
}
TreeNames::Some(payload)
}
}
fn unescape(value: &str, except: &[char]) -> String {
let mut result = String::with_capacity(value.len());
let mut iter = value.chars();
while let Some(c) = iter.next() {
match c {
'\\' => {
if let Some(c) = iter.next() {
if except.contains(&c) {
result.push('\\')
}
result.push(c);
}
}
c => result.push(c),
}
}
result
}
#[derive(Debug, Eq, PartialEq)]
pub struct TreeSelector<'a> {
pub node: Vec<Segment<'a>>,
pub property: Option<Segment<'a>>,
pub tree_names: Option<TreeNames<'a>>,
}
#[derive(Debug, Eq, PartialEq)]
pub struct ComponentSelector<'a> {
pub segments: Vec<Segment<'a>>,
}
#[derive(Debug, Eq, PartialEq)]
pub struct Selector<'a> {
pub component: ComponentSelector<'a>,
pub tree: TreeSelector<'a>,
}
impl From<Selector<'_>> for fdiagnostics::Selector {
fn from(mut selector: Selector<'_>) -> fdiagnostics::Selector {
let tree_names = selector.tree.tree_names.take();
fdiagnostics::Selector {
component_selector: Some(selector.component.into()),
tree_selector: Some(selector.tree.into()),
tree_names: tree_names.map(|names| names.into()),
..Default::default()
}
}
}
impl From<ComponentSelector<'_>> for fdiagnostics::ComponentSelector {
fn from(component_selector: ComponentSelector<'_>) -> fdiagnostics::ComponentSelector {
fdiagnostics::ComponentSelector {
moniker_segments: Some(
component_selector.segments.into_iter().map(|segment| segment.into()).collect(),
),
..Default::default()
}
}
}
impl From<TreeSelector<'_>> for fdiagnostics::TreeSelector {
fn from(tree_selector: TreeSelector<'_>) -> fdiagnostics::TreeSelector {
let node_path = tree_selector.node.into_iter().map(|s| s.into()).collect();
match tree_selector.property {
None => fdiagnostics::TreeSelector::SubtreeSelector(fdiagnostics::SubtreeSelector {
node_path,
}),
Some(property) => {
fdiagnostics::TreeSelector::PropertySelector(fdiagnostics::PropertySelector {
node_path,
target_properties: property.into(),
})
}
}
}
}
impl From<TreeNames<'_>> for fdiagnostics::TreeNames {
fn from(tree_names: TreeNames<'_>) -> fdiagnostics::TreeNames {
match tree_names {
TreeNames::All => fdiagnostics::TreeNames::All(fdiagnostics::All {}),
TreeNames::Some(names) => {
fdiagnostics::TreeNames::Some(names.iter().map(|n| n.to_string()).collect())
}
}
}
}
impl From<Segment<'_>> for fdiagnostics::StringSelector {
fn from(segment: Segment<'_>) -> fdiagnostics::StringSelector {
match segment {
Segment::ExactMatch(s) => fdiagnostics::StringSelector::ExactMatch(s.into_owned()),
Segment::Pattern(s) => fdiagnostics::StringSelector::StringPattern(s.into_owned()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[fuchsia::test]
fn convert_string_to_segment() {
assert_eq!(Segment::ExactMatch(Cow::Borrowed("abc")), "abc".into());
assert_eq!(Segment::Pattern("a*c".into()), "a*c".into());
assert_eq!(Segment::ExactMatch(Cow::Owned("ac*".into())), "ac\\*".into());
assert_eq!(Segment::Pattern("a\\*c*".into()), "a\\*c*".into());
}
}