diff --git a/application/apps/indexer/indexer_cli/src/interactive.rs b/application/apps/indexer/indexer_cli/src/interactive.rs index 49955ee6c..32a8e5a41 100644 --- a/application/apps/indexer/indexer_cli/src/interactive.rs +++ b/application/apps/indexer/indexer_cli/src/interactive.rs @@ -1,12 +1,13 @@ use crate::{duration_report, Instant}; use futures::{pin_mut, stream::StreamExt}; -use parsers::{dlt::DltParser, MessageStreamItem, ParseYield}; +use parsers::{ + dlt::DltParser, + nested_parser::{resolve_log_msg, ParseRestResolver}, + MessageStreamItem, ParseYield, +}; use processor::grabber::LineRange; use rustyline::{error::ReadlineError, DefaultEditor}; -use session::{ - parse_rest_resolver::{resolve_log_msg, ParseRestReslover}, - session::Session, -}; +use session::session::Session; use sources::{ factory::{DltParserSettings, FileFormat, ObserveOptions, ParserType}, producer::MessageProducer, @@ -49,7 +50,7 @@ pub(crate) async fn handle_interactive_session(input: Option) { let udp_source = UdpSource::new(RECEIVER, vec![]).await.unwrap(); let dlt_parser = DltParser::new(None, None, None, false); let mut dlt_msg_producer = MessageProducer::new(dlt_parser, udp_source, None); - let mut parse_reslover = ParseRestReslover::new(); + let mut parse_reslover = ParseRestResolver::new(); let msg_stream = dlt_msg_producer.as_stream(); pin_mut!(msg_stream); loop { diff --git a/application/apps/indexer/parsers/src/dlt/fmt.rs b/application/apps/indexer/parsers/src/dlt/fmt.rs index e645debd1..4c3d5dedd 100644 --- a/application/apps/indexer/parsers/src/dlt/fmt.rs +++ b/application/apps/indexer/parsers/src/dlt/fmt.rs @@ -28,11 +28,14 @@ use log::trace; use serde::ser::{Serialize, SerializeStruct, Serializer}; use std::{ - fmt::{self, Formatter, Write}, + fmt::{self, Display, Formatter, Write}, str, }; -use crate::{LogMessage, LogMessageContent, ResolveParseHint, TemplateLogMsg, TemplateLogMsgChunk}; +use crate::{ + nested_parser::ParseRestResolver, GeneralParseLogError, LogMessage, ParseLogSeverity, + ResolveParseHint, +}; const DLT_COLUMN_SENTINAL: char = '\u{0004}'; const DLT_ARGUMENT_SENTINAL: char = '\u{0005}'; @@ -538,6 +541,8 @@ impl<'a> FormattableMessage<'a> { } impl LogMessage for FormattableMessage<'_> { + type ParseError = GeneralParseLogError; + fn to_writer(&self, writer: &mut W) -> Result { let bytes = self.message.as_bytes(); let len = bytes.len(); @@ -561,7 +566,10 @@ impl LogMessage for FormattableMessage<'_> { /// context-id /// /// payload - fn try_resolve(&self) -> LogMessageContent { + fn try_resolve( + &self, + resolver: Option<&mut ParseRestResolver>, + ) -> Result { let mut msg = String::new(); // Taken from Documentation: string formatting is considered an infallible operation. // Thus we can ignore `fmt::Error` errors. @@ -620,25 +628,34 @@ impl LogMessage for FormattableMessage<'_> { }); if is_someip { - if let Some(slice) = slices.get(1) { - let template = TemplateLogMsg::new( - vec![ - TemplateLogMsgChunk::Text(msg), - TemplateLogMsgChunk::Bytes(slice.to_owned()), - ], - vec![ResolveParseHint::SomeIP], - ); - return LogMessageContent::Template(template); + if let Some(resolver) = resolver { + if let Some(slice) = slices.get(1) { + match resolver.try_resolve(slice, ResolveParseHint::SomeIP) { + Some(Ok(resolved)) => { + let _ = write!(msg, "{resolved}"); + return Ok(msg); + } + Some(Err(_)) | None => { + //TODO: Ignore nested Error while prototyping + } + } + } } } slices.iter().for_each(|slice| { let _ = write!(msg, "{}{:02X?}", DLT_ARGUMENT_SENTINAL, slice); }); + + return Err(GeneralParseLogError::new( + msg, + "Error while resolving Network trace payload".into(), + ParseLogSeverity::Error, + )); } } - msg.into() + Ok(msg) } } diff --git a/application/apps/indexer/parsers/src/dlt/mod.rs b/application/apps/indexer/parsers/src/dlt/mod.rs index fef9164e3..7bd71434d 100644 --- a/application/apps/indexer/parsers/src/dlt/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/mod.rs @@ -13,7 +13,7 @@ use dlt_core::{ parse::{dlt_consume_msg, dlt_message}, }; use serde::Serialize; -use std::{io::Write, ops::Range}; +use std::{convert::Infallible, fmt::Display, io::Write, ops::Range}; use self::{attachment::FtScanner, fmt::FormatOptions}; @@ -39,6 +39,8 @@ impl std::fmt::Display for RawMessage { } impl LogMessage for RangeMessage { + type ParseError = Infallible; + /// A RangeMessage only has range information and cannot serialize to bytes fn to_writer(&self, writer: &mut W) -> Result { writer.write_u64::(self.range.start as u64)?; @@ -46,20 +48,28 @@ impl LogMessage for RangeMessage { Ok(8 + 8) } - fn try_resolve(&self) -> crate::LogMessageContent { - self.into() + fn try_resolve( + &self, + _resolver: Option<&mut crate::nested_parser::ParseRestResolver>, + ) -> Result { + Ok(self) } } impl LogMessage for RawMessage { + type ParseError = Infallible; + fn to_writer(&self, writer: &mut W) -> Result { let len = self.content.len(); writer.write_all(&self.content)?; Ok(len) } - fn try_resolve(&self) -> crate::LogMessageContent { - self.into() + fn try_resolve( + &self, + _resolver: Option<&mut crate::nested_parser::ParseRestResolver>, + ) -> Result { + Ok(self) } } diff --git a/application/apps/indexer/parsers/src/lib.rs b/application/apps/indexer/parsers/src/lib.rs index 82d97923c..9c8527fd2 100644 --- a/application/apps/indexer/parsers/src/lib.rs +++ b/application/apps/indexer/parsers/src/lib.rs @@ -1,9 +1,11 @@ #![deny(unused_crate_dependencies)] pub mod dlt; +pub mod nested_parser; pub mod someip; pub mod text; +use nested_parser::ParseRestResolver; use serde::Serialize; -use std::{fmt::Display, io::Write}; +use std::{convert::Infallible, fmt::Display, io::Write}; use thiserror::Error; extern crate log; @@ -87,116 +89,100 @@ pub enum MessageStreamItem { Done, } -pub trait LogMessage: Serialize { - /// Serializes a message directly into a Writer - /// returns the size of the serialized message - fn to_writer(&self, writer: &mut W) -> Result; - - /// Tries to resolve the message to get its text representation. - /// - /// TODO: This function should return another optional field, containing information - /// about errors, warning ... - fn try_resolve(&self) -> LogMessageContent; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ParseLogSeverity { + Error, + Warn, + Info, } -#[derive(Debug, Clone)] -/// Represents The content of a log message after trying to resolve it. -pub enum LogMessageContent { - Text(String), - Template(TemplateLogMsg), +pub trait ParseLogMsgError { + fn parse_lossy(self) -> String; + fn severity(&self) -> ParseLogSeverity; + fn error_msg(&self) -> String; } -#[derive(Debug, Clone)] -/// Represents an unresolved log messages that contains chunks that needs to be resolved. -pub struct TemplateLogMsg { - chunks: Vec, - resolve_hints: Vec, -} +impl ParseLogMsgError for Infallible { + fn parse_lossy(self) -> String { + panic!("Infallible can't be instantiated") + } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -/// Gives Hint about how the payload rest can be resolved -pub enum ResolveParseHint { - /// The message needs to be parsed with SomeIP Parser. - SomeIP, + fn severity(&self) -> ParseLogSeverity { + panic!("Infallible can't be instantiated") + } + + fn error_msg(&self) -> String { + panic!("Infallible can't be instantiated") + } } #[derive(Debug, Clone)] -/// Represents a chunk in [`TemplateLogMsg`] -pub enum TemplateLogMsgChunk { - /// Resolved Chunk - Text(String), - /// Unresolved Chunk - Bytes(Vec), -} - -impl From for LogMessageContent -where - T: Display, -{ - fn from(value: T) -> Self { - Self::Text(value.to_string()) - } +//TODO AAZ: Move all those structs to a new module. It's getting crowded here. +pub struct GeneralParseLogError { + content: String, + err_msg: String, + severity: ParseLogSeverity, } -impl TemplateLogMsg { - pub fn new(chunks: Vec, resolve_hints: Vec) -> Self { +impl GeneralParseLogError { + pub fn new(content: String, err_msg: String, severity: ParseLogSeverity) -> Self { Self { - chunks, - resolve_hints, + content, + err_msg, + severity, } } - pub fn get_resolve_hints(&self) -> Vec { - self.resolve_hints.to_vec() - } + //TODO: Make sure this converting is enough. + pub fn from_parser_err(bytes: &[u8], severity: ParseLogSeverity, err: Error) -> Self { + let content = format!("{bytes:?}"); + let err_msg = match err { + Error::Parse(parse_err) => format!("Nested Parser Error: Parse Error: {parse_err}"), + Error::Incomplete => "Nested Parser Error: Incomplete".into(), + Error::Eof => "Nested Parser Error: Eof".into(), + }; - /// Applies the given [`FnMut`] on the unresolved chunks, replacing them with texts if succeed. - /// This function will return a String once chunks get resolved. - /// - /// * `parse_fn`: Function to apply on the unresolved chunks. - pub fn try_resolve(&mut self, mut parse_fn: F) -> Option - where - F: FnMut(&[u8]) -> Option, - { - let mut all_resolved = true; - for ch in self.chunks.iter_mut() { - match ch { - TemplateLogMsgChunk::Text(_) => continue, - TemplateLogMsgChunk::Bytes(bytes) => match parse_fn(&bytes) { - Some(resolved) => *ch = TemplateLogMsgChunk::Text(resolved), - None => all_resolved = false, - }, - } + Self { + content, + severity, + err_msg, } + } +} - if all_resolved { - self.chunks - .iter() - .map(|ch| match ch { - TemplateLogMsgChunk::Text(msg) => msg, - TemplateLogMsgChunk::Bytes(_) => panic!("All must be resolved"), - }) - .cloned() - .reduce(|mut acc, msg| { - acc.push_str(&msg); - acc - }) - } else { - None - } +impl ParseLogMsgError for GeneralParseLogError { + fn parse_lossy(self) -> String { + self.content } - /// Concatenates the chunks to a string, replacing the unresolved chunks with their bytes - /// representation. - pub fn resolve_lossy(self) -> String { - self.chunks - .into_iter() - .fold(String::new(), |mut acc, ch| match ch { - TemplateLogMsgChunk::Text(msg) => { - acc.push_str(&msg); - acc - } - TemplateLogMsgChunk::Bytes(bytes) => format!("{acc} {bytes:?}"), - }) + fn severity(&self) -> ParseLogSeverity { + self.severity } + + fn error_msg(&self) -> String { + self.err_msg.to_owned() + } +} + +pub trait LogMessage: Serialize { + type ParseError: ParseLogMsgError; + /// Serializes a message directly into a Writer + /// returns the size of the serialized message + fn to_writer(&self, writer: &mut W) -> Result; + + /// Tries to resolve the message to get its text representation, with in optional help from + /// [`ParseRestResolver`] for the parts which can't be parsed. + fn try_resolve( + &self, + // TODO: Remember the point of making the resolver optional, is to avoid infinite + // recursions in case of parsers calling each others + resolver: Option<&mut ParseRestResolver>, + ) -> Result; +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +/// Gives Hint about how the payload rest can be resolved +pub enum ResolveParseHint { + /// The message needs to be parsed with SomeIP Parser. + SomeIP, } diff --git a/application/apps/indexer/parsers/src/nested_parser.rs b/application/apps/indexer/parsers/src/nested_parser.rs new file mode 100644 index 000000000..0a434dc81 --- /dev/null +++ b/application/apps/indexer/parsers/src/nested_parser.rs @@ -0,0 +1,90 @@ +use std::convert::Infallible; + +use crate::GeneralParseLogError; +use crate::ParseLogMsgError; +use crate::ParseLogSeverity; +use crate::Parser; +use crate::{someip::SomeipParser, LogMessage, ParseYield, ResolveParseHint}; + +#[derive(Default)] +pub struct ParseRestResolver { + someip_praser: Option, +} + +impl ParseRestResolver { + pub fn new() -> Self { + Self::default() + } + + /// Sets SomeIP parser on the resolver + pub fn with_someip_parser(&mut self, someip_praser: SomeipParser) -> &mut Self { + self.someip_praser = Some(someip_praser); + self + } + + /// Tries to resolve the given error returning the parsed string if succeeded. + pub fn try_resolve( + &mut self, + bytes: &[u8], + resolve_hint: ResolveParseHint, + ) -> Option> { + match resolve_hint { + ResolveParseHint::SomeIP => { + let parser = self.someip_praser.as_mut()?; + //TODO: Proper error handling for parser return + let p_yield = match parser.parse(bytes, None) { + Ok(res) => res.1?, + Err(err) => { + let err = GeneralParseLogError::from_parser_err( + bytes, + ParseLogSeverity::Error, + err, + ); + + return Some(Err(err)); + } + }; + match p_yield { + ParseYield::Message(item) => { + let res = match item.try_resolve(Some(self)) { + Ok(parsed) => parsed, + Err(err) => { + if cfg!(debug_assertions) { + ensure_infalliable(err); + } + panic!("Infallible Error can't be created") + } + }; + + Some(Ok(res.to_string())) + } + // Ignore other parse types for now + ParseYield::Attachment(_) | ParseYield::MessageAndAttachment(_) => { + let err = GeneralParseLogError::new( + format!("{bytes:?}"), + "Found attachment in nested payload".into(), + ParseLogSeverity::Error, + ); + Some(Err(err)) + } + } + } + } + } +} + +// Ensure the type of given argument is Infallible, raising a compile time error if not. +fn ensure_infalliable(_err: Infallible) {} + +/// Get the text message of [`LogMessage`], resolving its rest payloads if existed when possible, +/// TODO: Otherwise it should save the error to the faulty messages store, which need to be +/// implemented as well :) +pub fn resolve_log_msg(item: T, err_resolver: &mut ParseRestResolver) -> String { + match item.try_resolve(Some(err_resolver)) { + Ok(item) => item.to_string(), + Err(err) => { + //TODO: Add error to errors cache. + err.parse_lossy() + } + } +} diff --git a/application/apps/indexer/parsers/src/someip.rs b/application/apps/indexer/parsers/src/someip.rs index 79e3bc66a..a216452a1 100644 --- a/application/apps/indexer/parsers/src/someip.rs +++ b/application/apps/indexer/parsers/src/someip.rs @@ -1,5 +1,11 @@ -use crate::{Error, LogMessage, LogMessageContent, ParseYield, Parser}; -use std::{borrow::Cow, fmt, fmt::Display, io::Write, path::PathBuf}; +use crate::{Error, LogMessage, ParseYield, Parser}; +use std::{ + borrow::Cow, + convert::Infallible, + fmt::{self, Display}, + io::Write, + path::PathBuf, +}; use someip_messages::*; use someip_payload::{ @@ -325,13 +331,18 @@ impl SomeipLogMessage { } impl LogMessage for SomeipLogMessage { + type ParseError = Infallible; + fn to_writer(&self, writer: &mut W) -> Result { writer.write_all(&self.bytes)?; Ok(self.bytes.len()) } - fn try_resolve(&self) -> LogMessageContent { - self.into() + fn try_resolve( + &self, + _resolver: Option<&mut crate::nested_parser::ParseRestResolver>, + ) -> Result { + Ok(self) } } diff --git a/application/apps/indexer/parsers/src/text.rs b/application/apps/indexer/parsers/src/text.rs index a59aad423..8022fbdce 100644 --- a/application/apps/indexer/parsers/src/text.rs +++ b/application/apps/indexer/parsers/src/text.rs @@ -1,6 +1,6 @@ -use crate::{Error, LogMessage, LogMessageContent, ParseYield, Parser}; +use crate::{Error, LogMessage, ParseYield, Parser}; use serde::Serialize; -use std::{fmt, io::Write}; +use std::{convert::Infallible, fmt, io::Write}; pub struct StringTokenizer {} @@ -16,14 +16,19 @@ impl fmt::Display for StringMessage { } impl LogMessage for StringMessage { + type ParseError = Infallible; + fn to_writer(&self, writer: &mut W) -> Result { let len = self.content.len(); writer.write_all(self.content.as_bytes())?; Ok(len) } - fn try_resolve(&self) -> LogMessageContent { - self.into() + fn try_resolve( + &self, + _resolver: Option<&mut crate::nested_parser::ParseRestResolver>, + ) -> Result { + Ok(self) } } diff --git a/application/apps/indexer/session/src/handlers/observing/mod.rs b/application/apps/indexer/session/src/handlers/observing/mod.rs index 3dd96eb90..8a780b79c 100644 --- a/application/apps/indexer/session/src/handlers/observing/mod.rs +++ b/application/apps/indexer/session/src/handlers/observing/mod.rs @@ -2,13 +2,13 @@ use std::path::PathBuf; use crate::{ operations::{OperationAPI, OperationResult}, - parse_rest_resolver::{resolve_log_msg, ParseRestReslover}, state::SessionStateAPI, tail, }; use log::trace; use parsers::{ dlt::{fmt::FormatOptions, DltParser}, + nested_parser::{resolve_log_msg, ParseRestResolver}, someip::SomeipParser, text::StringTokenizer, LogMessage, MessageStreamItem, ParseYield, Parser, @@ -78,7 +78,7 @@ async fn run_source_intern( rx_sde: Option, rx_tail: Option>>, ) -> OperationResult<()> { - let mut parse_err_resolver = ParseRestReslover::new(); + let mut parse_rest_resolver = ParseRestResolver::new(); match parser { ParserType::SomeIp(settings) => { let someip_parser = match &settings.fibex_file_paths { @@ -94,7 +94,7 @@ async fn run_source_intern( source_id, producer, rx_tail, - &mut parse_err_resolver, + &mut parse_rest_resolver, ) .await } @@ -106,7 +106,7 @@ async fn run_source_intern( source_id, producer, rx_tail, - &mut parse_err_resolver, + &mut parse_rest_resolver, ) .await } @@ -126,7 +126,7 @@ async fn run_source_intern( } None => SomeipParser::new(), }; - parse_err_resolver.with_someip_parser(someip_parse); + parse_rest_resolver.with_someip_parser(someip_parse); run_producer( operation_api, @@ -134,7 +134,7 @@ async fn run_source_intern( source_id, producer, rx_tail, - &mut parse_err_resolver, + &mut parse_rest_resolver, ) .await } @@ -147,7 +147,7 @@ async fn run_producer, S: ByteSource>( source_id: u16, mut producer: MessageProducer, mut rx_tail: Option>>, - parse_err_resolver: &mut ParseRestReslover, + parse_rest_resolver: &mut ParseRestResolver, ) -> OperationResult<()> { use log::debug; state.set_session_file(None).await?; @@ -175,7 +175,7 @@ async fn run_producer, S: ByteSource>( Next::Item(item) => { match item { MessageStreamItem::Item(ParseYield::Message(item)) => { - let msg = resolve_log_msg(item, parse_err_resolver); + let msg = resolve_log_msg(item, parse_rest_resolver); state .write_session_file(source_id, format!("{msg}\n")) .await?; @@ -184,7 +184,7 @@ async fn run_producer, S: ByteSource>( item, attachment, ))) => { - let msg = resolve_log_msg(item, parse_err_resolver); + let msg = resolve_log_msg(item, parse_rest_resolver); state .write_session_file(source_id, format!("{msg}\n")) .await?; diff --git a/application/apps/indexer/session/src/lib.rs b/application/apps/indexer/session/src/lib.rs index 9e3d0d32b..45a70490c 100644 --- a/application/apps/indexer/session/src/lib.rs +++ b/application/apps/indexer/session/src/lib.rs @@ -2,7 +2,6 @@ pub mod events; mod handlers; pub mod operations; -pub mod parse_rest_resolver; pub mod paths; pub mod progress; pub mod session; diff --git a/application/apps/indexer/session/src/parse_rest_resolver.rs b/application/apps/indexer/session/src/parse_rest_resolver.rs deleted file mode 100644 index ceb958249..000000000 --- a/application/apps/indexer/session/src/parse_rest_resolver.rs +++ /dev/null @@ -1,64 +0,0 @@ -use parsers::{someip::SomeipParser, LogMessage, Parser, TemplateLogMsg}; - -#[derive(Default)] -pub struct ParseRestReslover { - someip_praser: Option, -} - -impl ParseRestReslover { - pub fn new() -> Self { - Self::default() - } - - /// Sets SomeIP parser on the resolver - pub fn with_someip_parser(&mut self, someip_praser: SomeipParser) -> &mut Self { - self.someip_praser = Some(someip_praser); - self - } - - /// Tries to resolve the given error returning the parsed string if succeeded. - pub fn resolve_log_template(&mut self, template: &mut TemplateLogMsg) -> Option { - for hint in template.get_resolve_hints() { - match hint { - parsers::ResolveParseHint::SomeIP => { - if let Some(p) = self.someip_praser.as_mut() { - let res = template.try_resolve(|bytes| { - let p_yield = p.parse(bytes, None).ok()?.1?; - match p_yield { - parsers::ParseYield::Message(item) => match item.try_resolve() { - parsers::LogMessageContent::Text(msg) => Some(msg), - // Ignore nested errors for now - parsers::LogMessageContent::Template(_) => None, - }, - // Ignore other parse types for now - parsers::ParseYield::Attachment(_) => None, - parsers::ParseYield::MessageAndAttachment(_) => None, - } - }); - - if res.is_some() { - return res; - } - } - } - } - } - None - } -} - -/// Get the text message of [`LogMessage`], resolving its rest payloads if existed when possible, -/// TODO: Otherwise it should save the error to the faulty messages store, which need to be -/// implemented as well :) -pub fn resolve_log_msg(item: T, err_resolver: &mut ParseRestReslover) -> String { - match item.try_resolve() { - parsers::LogMessageContent::Text(msg) => msg, - parsers::LogMessageContent::Template(mut template) => { - if let Some(resolved) = err_resolver.resolve_log_template(&mut template) { - return resolved; - } - //TODO: Add message to the faulty messages once implemented. - template.resolve_lossy() - } - } -}