diff --git a/application/apps/indexer/indexer_cli/src/interactive.rs b/application/apps/indexer/indexer_cli/src/interactive.rs index 28e2d15e2..49955ee6c 100644 --- a/application/apps/indexer/indexer_cli/src/interactive.rs +++ b/application/apps/indexer/indexer_cli/src/interactive.rs @@ -4,7 +4,7 @@ use parsers::{dlt::DltParser, MessageStreamItem, ParseYield}; use processor::grabber::LineRange; use rustyline::{error::ReadlineError, DefaultEditor}; use session::{ - parse_err::{get_log_text, ParseErrorReslover}, + parse_rest_resolver::{resolve_log_msg, ParseRestReslover}, session::Session, }; use sources::{ @@ -49,7 +49,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 = ParseErrorReslover::new(); + let mut parse_reslover = ParseRestReslover::new(); let msg_stream = dlt_msg_producer.as_stream(); pin_mut!(msg_stream); loop { @@ -61,11 +61,11 @@ pub(crate) async fn handle_interactive_session(input: Option) { item = msg_stream.next() => { match item { Some((_, MessageStreamItem::Item(ParseYield::Message(item)))) => { - let msg = get_log_text(item, &mut parse_reslover); + let msg = resolve_log_msg(item, &mut parse_reslover); println!("msg: {msg}"); } Some((_, MessageStreamItem::Item(ParseYield::MessageAndAttachment((item, attachment))))) => { - let msg = get_log_text(item, &mut parse_reslover); + let msg = resolve_log_msg(item, &mut parse_reslover); println!("msg: {msg}, attachment: {attachment:?}"); } Some((_, MessageStreamItem::Item(ParseYield::Attachment(attachment)))) => { diff --git a/application/apps/indexer/parsers/src/dlt/fmt.rs b/application/apps/indexer/parsers/src/dlt/fmt.rs index a9c26dd94..e645debd1 100644 --- a/application/apps/indexer/parsers/src/dlt/fmt.rs +++ b/application/apps/indexer/parsers/src/dlt/fmt.rs @@ -32,7 +32,7 @@ use std::{ str, }; -use crate::{LogMessage, ParseLogError, ResolveErrorHint, ToTextResult}; +use crate::{LogMessage, LogMessageContent, ResolveParseHint, TemplateLogMsg, TemplateLogMsgChunk}; const DLT_COLUMN_SENTINAL: char = '\u{0004}'; const DLT_ARGUMENT_SENTINAL: char = '\u{0005}'; @@ -538,8 +538,6 @@ impl<'a> FormattableMessage<'a> { } impl LogMessage for FormattableMessage<'_> { - const CAN_ERROR: bool = true; - fn to_writer(&self, writer: &mut W) -> Result { let bytes = self.message.as_bytes(); let len = bytes.len(); @@ -563,7 +561,7 @@ impl LogMessage for FormattableMessage<'_> { /// context-id /// /// payload - fn to_text(&self) -> ToTextResult { + fn try_resolve(&self) -> LogMessageContent { let mut msg = String::new(); // Taken from Documentation: string formatting is considered an infallible operation. // Thus we can ignore `fmt::Error` errors. @@ -623,12 +621,14 @@ impl LogMessage for FormattableMessage<'_> { if is_someip { if let Some(slice) = slices.get(1) { - let err = ParseLogError::new( - slice.to_owned(), - crate::ParseErrorType::Other("Need Some IP".into()), - Some(ResolveErrorHint::SomeIP), + let template = TemplateLogMsg::new( + vec![ + TemplateLogMsgChunk::Text(msg), + TemplateLogMsgChunk::Bytes(slice.to_owned()), + ], + vec![ResolveParseHint::SomeIP], ); - return ToTextResult::new(msg, Some(err)); + return LogMessageContent::Template(template); } } diff --git a/application/apps/indexer/parsers/src/dlt/mod.rs b/application/apps/indexer/parsers/src/dlt/mod.rs index 34f899084..fef9164e3 100644 --- a/application/apps/indexer/parsers/src/dlt/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/mod.rs @@ -39,8 +39,6 @@ impl std::fmt::Display for RawMessage { } impl LogMessage for RangeMessage { - const CAN_ERROR: bool = false; - /// 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)?; @@ -48,21 +46,19 @@ impl LogMessage for RangeMessage { Ok(8 + 8) } - fn to_text(&self) -> crate::ToTextResult { + fn try_resolve(&self) -> crate::LogMessageContent { self.into() } } impl LogMessage for RawMessage { - const CAN_ERROR: bool = false; - fn to_writer(&self, writer: &mut W) -> Result { let len = self.content.len(); writer.write_all(&self.content)?; Ok(len) } - fn to_text(&self) -> crate::ToTextResult { + fn try_resolve(&self) -> crate::LogMessageContent { self.into() } } diff --git a/application/apps/indexer/parsers/src/lib.rs b/application/apps/indexer/parsers/src/lib.rs index 1d7122da9..82d97923c 100644 --- a/application/apps/indexer/parsers/src/lib.rs +++ b/application/apps/indexer/parsers/src/lib.rs @@ -78,99 +78,125 @@ pub enum ByteRepresentation { Range((usize, usize)), } -#[derive(Debug, Clone)] -pub enum ParseErrorType { - Fmt(String), - Other(String), +#[derive(Debug)] +pub enum MessageStreamItem { + Item(ParseYield), + Skipped, + Incomplete, + Empty, + Done, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -/// Gives Hint about how this error can be resolved by processor -pub enum ResolveErrorHint { - /// The message needs to be parsed with SomeIP Parser. - SomeIP, +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)] -pub struct ParseLogError { - pub remain_bytes: Vec, - pub error_type: ParseErrorType, - pub resolve_hint: Option, -} - -impl ParseLogError { - pub fn new( - remain_bytes: Vec, - error_type: ParseErrorType, - resolve_hint: Option, - ) -> Self { - Self { - remain_bytes, - error_type, - resolve_hint, - } - } +/// Represents The content of a log message after trying to resolve it. +pub enum LogMessageContent { + Text(String), + Template(TemplateLogMsg), } -impl Display for ParseLogError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match &self.error_type { - ParseErrorType::Other(msg) | ParseErrorType::Fmt(msg) => write!(f, "{msg}"), - } - } +#[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 From for ParseLogError { - fn from(value: std::fmt::Error) -> Self { - Self { - remain_bytes: Vec::new(), - error_type: ParseErrorType::Fmt(value.to_string()), - resolve_hint: None, - } - } +#[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, } -impl std::error::Error for ParseLogError {} - #[derive(Debug, Clone)] -pub struct ToTextResult { - pub msg: String, - pub error: Option, -} - -impl ToTextResult { - pub fn new(msg: String, error: Option) -> Self { - Self { msg, error } - } +/// Represents a chunk in [`TemplateLogMsg`] +pub enum TemplateLogMsgChunk { + /// Resolved Chunk + Text(String), + /// Unresolved Chunk + Bytes(Vec), } -impl From for ToTextResult +impl From for LogMessageContent where T: Display, { fn from(value: T) -> Self { - Self::new(value.to_string(), None) + Self::Text(value.to_string()) } } -pub trait LogMessage: Serialize { - //TODO AAZ: Measure this an remove if rust already optimize the code without it. - /// Indicates that parsing this struct to text can error. - const CAN_ERROR: bool; +impl TemplateLogMsg { + pub fn new(chunks: Vec, resolve_hints: Vec) -> Self { + Self { + chunks, + resolve_hints, + } + } - /// Serializes a message directly into a Writer - /// returns the size of the serialized message - fn to_writer(&self, writer: &mut W) -> Result; + pub fn get_resolve_hints(&self) -> Vec { + self.resolve_hints.to_vec() + } - /// Get the text representation of this message. - fn to_text(&self) -> ToTextResult; -} + /// 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, + }, + } + } -#[derive(Debug)] -pub enum MessageStreamItem { - Item(ParseYield), - Skipped, - Incomplete, - Empty, - Done, + 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 + } + } + + /// 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:?}"), + }) + } } diff --git a/application/apps/indexer/parsers/src/someip.rs b/application/apps/indexer/parsers/src/someip.rs index 438c383b6..79e3bc66a 100644 --- a/application/apps/indexer/parsers/src/someip.rs +++ b/application/apps/indexer/parsers/src/someip.rs @@ -1,4 +1,4 @@ -use crate::{Error, LogMessage, ParseYield, Parser, ToTextResult}; +use crate::{Error, LogMessage, LogMessageContent, ParseYield, Parser}; use std::{borrow::Cow, fmt, fmt::Display, io::Write, path::PathBuf}; use someip_messages::*; @@ -325,14 +325,12 @@ impl SomeipLogMessage { } impl LogMessage for SomeipLogMessage { - const CAN_ERROR: bool = false; - fn to_writer(&self, writer: &mut W) -> Result { writer.write_all(&self.bytes)?; Ok(self.bytes.len()) } - fn to_text(&self) -> ToTextResult { + fn try_resolve(&self) -> LogMessageContent { self.into() } } diff --git a/application/apps/indexer/parsers/src/text.rs b/application/apps/indexer/parsers/src/text.rs index 59f388023..a59aad423 100644 --- a/application/apps/indexer/parsers/src/text.rs +++ b/application/apps/indexer/parsers/src/text.rs @@ -1,4 +1,4 @@ -use crate::{Error, LogMessage, ParseYield, Parser, ToTextResult}; +use crate::{Error, LogMessage, LogMessageContent, ParseYield, Parser}; use serde::Serialize; use std::{fmt, io::Write}; @@ -16,15 +16,13 @@ impl fmt::Display for StringMessage { } impl LogMessage for StringMessage { - const CAN_ERROR: bool = false; - fn to_writer(&self, writer: &mut W) -> Result { let len = self.content.len(); writer.write_all(self.content.as_bytes())?; Ok(len) } - fn to_text(&self) -> ToTextResult { + fn try_resolve(&self) -> LogMessageContent { self.into() } } diff --git a/application/apps/indexer/session/src/handlers/observing/mod.rs b/application/apps/indexer/session/src/handlers/observing/mod.rs index 95cae7298..3dd96eb90 100644 --- a/application/apps/indexer/session/src/handlers/observing/mod.rs +++ b/application/apps/indexer/session/src/handlers/observing/mod.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; use crate::{ operations::{OperationAPI, OperationResult}, - parse_err::{get_log_text, ParseErrorReslover}, + parse_rest_resolver::{resolve_log_msg, ParseRestReslover}, state::SessionStateAPI, tail, }; @@ -78,7 +78,7 @@ async fn run_source_intern( rx_sde: Option, rx_tail: Option>>, ) -> OperationResult<()> { - let mut parse_err_resolver = ParseErrorReslover::new(); + let mut parse_err_resolver = ParseRestReslover::new(); match parser { ParserType::SomeIp(settings) => { let someip_parser = match &settings.fibex_file_paths { @@ -119,6 +119,7 @@ async fn run_source_intern( settings.with_storage_header, ); let producer = MessageProducer::new(dlt_parser, source, rx_sde); + let someip_parse = match &settings.fibex_file_paths { Some(paths) => { SomeipParser::from_fibex_files(paths.iter().map(PathBuf::from).collect()) @@ -126,6 +127,7 @@ async fn run_source_intern( None => SomeipParser::new(), }; parse_err_resolver.with_someip_parser(someip_parse); + run_producer( operation_api, state, @@ -145,7 +147,7 @@ async fn run_producer, S: ByteSource>( source_id: u16, mut producer: MessageProducer, mut rx_tail: Option>>, - parse_err_resolver: &mut ParseErrorReslover, + parse_err_resolver: &mut ParseRestReslover, ) -> OperationResult<()> { use log::debug; state.set_session_file(None).await?; @@ -173,7 +175,7 @@ async fn run_producer, S: ByteSource>( Next::Item(item) => { match item { MessageStreamItem::Item(ParseYield::Message(item)) => { - let msg = get_log_text(item, parse_err_resolver); + let msg = resolve_log_msg(item, parse_err_resolver); state .write_session_file(source_id, format!("{msg}\n")) .await?; @@ -182,7 +184,7 @@ async fn run_producer, S: ByteSource>( item, attachment, ))) => { - let msg = get_log_text(item, parse_err_resolver); + let msg = resolve_log_msg(item, parse_err_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 42dd87eb3..9e3d0d32b 100644 --- a/application/apps/indexer/session/src/lib.rs +++ b/application/apps/indexer/session/src/lib.rs @@ -2,7 +2,7 @@ pub mod events; mod handlers; pub mod operations; -pub mod parse_err; +pub mod parse_rest_resolver; pub mod paths; pub mod progress; pub mod session; diff --git a/application/apps/indexer/session/src/parse_err.rs b/application/apps/indexer/session/src/parse_err.rs deleted file mode 100644 index a1a909444..000000000 --- a/application/apps/indexer/session/src/parse_err.rs +++ /dev/null @@ -1,66 +0,0 @@ -use parsers::{someip::SomeipParser, LogMessage, ParseLogError, Parser}; - -#[derive(Default)] -pub struct ParseErrorReslover { - someip_praser: Option, -} - -impl ParseErrorReslover { - 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_err(&mut self, error: &ParseLogError) -> Option { - match error.resolve_hint { - Some(parsers::ResolveErrorHint::SomeIP) => self - .someip_praser - .as_mut() - .and_then(|parser| { - parser - .parse(&error.remain_bytes, None) - .ok() - .and_then(|res| res.1) - }) - .and_then(|a| match a { - // TODO: Handle someip parser errors after prototyping. - parsers::ParseYield::Message(msg) => Some(msg.to_text().msg), - parsers::ParseYield::Attachment(_) => None, - parsers::ParseYield::MessageAndAttachment((msg, _att)) => { - Some(msg.to_text().msg) - } - }), - None => None, - } - } -} - -/// Get the text message of [`LogMessage`], resolving parse text errors if possible, -/// TODO: Otherwise it should save the error to the faulty messages store, which need to be -/// implemented as well :) -pub fn get_log_text(item: T, err_resolver: &mut ParseErrorReslover) -> String { - let text_res = item.to_text(); - if T::CAN_ERROR { - let mut msg = text_res.msg; - if let Some(err_info) = text_res.error { - match err_resolver.resolve_err(&err_info) { - Some(resloved_msg) => { - msg.push_str(&resloved_msg); - } - None => { - //TODO: Item with error details should be reported faulty messages store. - msg = format!("{msg}: Unknow Error bytes: {:?}", err_info.remain_bytes); - } - } - } - msg - } else { - text_res.msg - } -} diff --git a/application/apps/indexer/session/src/parse_rest_resolver.rs b/application/apps/indexer/session/src/parse_rest_resolver.rs new file mode 100644 index 000000000..ceb958249 --- /dev/null +++ b/application/apps/indexer/session/src/parse_rest_resolver.rs @@ -0,0 +1,64 @@ +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() + } + } +}