blob: 5870cab4fddec2312f0978de417b60fe286fdebf [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use fidl_fuchsia_diagnostics as fdiagnostics;
use std::{borrow::Cow, fmt::Debug};
/// Severity
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Severity {
Trace,
Debug,
Info,
Warn,
Error,
Fatal,
}
/// Identifier
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Identifier {
Filename,
LifecycleEventType,
LineNumber,
Pid,
Severity,
Tags,
Tid,
Timestamp,
}
/// Supported comparison operators.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ComparisonOperator {
Equal,
GreaterEq,
Greater,
LessEq,
Less,
NotEq,
}
/// Supported inclusion operators.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum InclusionOperator {
HasAny,
HasAll,
In,
}
/// Supported operators.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Operator {
Inclusion(InclusionOperator),
Comparison(ComparisonOperator),
}
/// Accepted right-hand-side values that can be used in an operation.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Value<'a> {
Severity(Severity),
StringLiteral(&'a str),
Number(u64),
}
impl Value<'_> {
fn ty(&self) -> ValueType {
match self {
Value::Severity(_) => ValueType::Severity,
Value::StringLiteral(_) => ValueType::StringLiteral,
Value::Number(_) => ValueType::Number,
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum ValueType {
Severity,
StringLiteral,
Number,
}
/// Holds a single value or a vector of values of type `T`.
#[derive(Debug, Eq, PartialEq)]
pub enum OneOrMany<T: Debug + Eq + PartialEq> {
One(T),
Many(Vec<T>),
}
impl OneOrMany<Value<'_>> {
pub(crate) fn ty(&self) -> OneOrMany<ValueType> {
match self {
Self::One(value) => OneOrMany::One(value.ty()),
Self::Many(values) => OneOrMany::Many(values.into_iter().map(|v| v.ty()).collect()),
}
}
}
/// A single filter expression in a metadata selector.
#[derive(Debug, Eq, PartialEq)]
pub struct FilterExpression<'a> {
pub identifier: Identifier,
pub operator: Operator,
pub value: OneOrMany<Value<'a>>,
}
/// Represents a metadata selector, which consists of a list of filters.
#[derive(Debug, Eq, PartialEq)]
pub struct MetadataSelector<'a>(pub(crate) Vec<FilterExpression<'a>>);
impl<'a> MetadataSelector<'a> {
pub fn new(filters: Vec<FilterExpression<'a>>) -> Self {
Self(filters)
}
pub fn filters(&self) -> &[FilterExpression<'a>] {
self.0.as_slice()
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum Segment<'a> {
ExactMatch(Cow<'a, str>),
Pattern(&'a str),
}
fn contains_unescaped_wildcard(s: &str) -> bool {
let mut iter = s.chars();
while let Some(c) = iter.next() {
match c {
'*' => return true,
'\\' => {
// skip escaped characters
let _ = iter.next();
}
_ => {}
}
}
false
}
impl<'a> Into<Segment<'a>> for &'a str {
fn into(self) -> Segment<'a> {
if contains_unescaped_wildcard(self) {
return Segment::Pattern(self);
}
if !self.contains('\\') {
return Segment::ExactMatch(Cow::from(self));
}
let mut result = String::with_capacity(self.len());
let mut iter = self.chars();
while let Some(c) = iter.next() {
match c {
'\\' => {
// push unescaped character since we are constructing an exact match.
if let Some(c) = iter.next() {
result.push(c);
}
}
c => result.push(c),
}
}
Segment::ExactMatch(Cow::from(result))
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct TreeSelector<'a> {
pub node: Vec<Segment<'a>>,
pub property: Option<Segment<'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>,
pub metadata: Option<MetadataSelector<'a>>,
}
impl Into<fdiagnostics::Selector> for Selector<'_> {
fn into(self) -> fdiagnostics::Selector {
fdiagnostics::Selector {
component_selector: Some(self.component.into()),
tree_selector: Some(self.tree.into()),
..fdiagnostics::Selector::EMPTY // TODO(fxbug.dev/55118): add metadata.
}
}
}
impl Into<fdiagnostics::ComponentSelector> for ComponentSelector<'_> {
fn into(self) -> fdiagnostics::ComponentSelector {
fdiagnostics::ComponentSelector {
moniker_segments: Some(
self.segments.into_iter().map(|segment| segment.into()).collect(),
),
..fdiagnostics::ComponentSelector::EMPTY
}
}
}
impl Into<fdiagnostics::TreeSelector> for TreeSelector<'_> {
fn into(self) -> fdiagnostics::TreeSelector {
let node_path = self.node.into_iter().map(|s| s.into()).collect();
match self.property {
None => fdiagnostics::TreeSelector::SubtreeSelector(fdiagnostics::SubtreeSelector {
node_path,
}),
Some(property) => {
fdiagnostics::TreeSelector::PropertySelector(fdiagnostics::PropertySelector {
node_path,
target_properties: property.into(),
})
}
}
}
}
impl Into<fdiagnostics::StringSelector> for Segment<'_> {
fn into(self) -> fdiagnostics::StringSelector {
match self {
Segment::ExactMatch(s) => fdiagnostics::StringSelector::ExactMatch(s.into_owned()),
Segment::Pattern(s) => fdiagnostics::StringSelector::StringPattern(s.to_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"), "a*c".into());
assert_eq!(Segment::ExactMatch(Cow::Owned("ac*".into())), "ac\\*".into());
assert_eq!(Segment::Pattern("a\\*c*"), "a\\*c*".into());
}
}