use regex::bytes::Regex; use serde::{Deserialize, Serialize}; use crate::tui::{ log_viewer::{ LogViewer, input::{FieldMatcher, InputTarget}, }, model::{FieldsName, LogEntry}, }; mod serialize_regex { use regex::bytes::Regex; use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error}; pub fn serialize(r: &Regex, serializer: S) -> Result { r.as_str().serialize(serializer) } pub fn deserialize<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; Regex::new(&s).map_err(|e| D::Error::custom(e.to_string())) } } #[derive(Serialize, Deserialize, Debug)] pub enum MatcherValue { Exact(String), Regex(#[serde(with = "serialize_regex")] Regex), Prefix(String), Contains(String), } impl MatcherValue { pub fn from_field_matcher(fm: FieldMatcher, selected: Option) -> Option { match fm { FieldMatcher::EqualTo => Some(Self::Exact(selected?)), FieldMatcher::Prefix(p) => Some(Self::Prefix(p)), FieldMatcher::Regex(r) => Some(Self::Regex(Regex::new(&r).ok()?)), FieldMatcher::Contains(c) => Some(Self::Contains(c)), } } pub fn matches(&self, v: &str) -> bool { match self { MatcherValue::Exact(e) => e == v, MatcherValue::Regex(regex) => regex.is_match(v.as_bytes()), MatcherValue::Prefix(p) => v.starts_with(p), MatcherValue::Contains(c) => v.contains(c), } } } #[derive(Serialize, Deserialize, Debug)] pub enum Matcher { Field { span: FieldsName, name: String, value: MatcherValue, }, Message { value: MatcherValue, }, Specific { hash: u64, }, } impl Matcher { pub fn matches(&self, entry: &LogEntry) -> bool { match self { Matcher::Specific { hash } => entry.hash() == *hash, Matcher::Field { span, name, value } => entry .spans() .find(span, name) .is_some_and(|v| value.matches(v)), Matcher::Message { value } => entry.message_or_name().is_some_and(|v| value.matches(v)), } } pub fn from_input(target: InputTarget, lv: &LogViewer) -> Option { match target { InputTarget::Fields(fm) => { let (span, name, value) = lv.get_selected_field()?; Some(Self::Field { span, name, value: MatcherValue::from_field_matcher(fm?, Some(value))?, }) } InputTarget::Text(fm) => Some(Self::Message { value: MatcherValue::from_field_matcher( fm, lv.selected() .as_ref() .and_then(|(i, _)| i.message_or_name()) .map(|i| i.to_string()), )?, }), InputTarget::This => { if lv .selected() .is_none_or(|(i, _)| !i.can_be_inlined(&lv.filters)) { return None; } lv.selected() .map(|(i, _)| Self::Specific { hash: i.hash() }) } InputTarget::Surround => todo!(), } } } #[derive(Clone, Serialize, Deserialize, Debug)] pub enum FilterKind { Inline, Remove, } #[derive(Serialize, Deserialize, Debug)] pub struct Filter { pub matcher: Matcher, pub kind: FilterKind, }