Skip to content

Commit

Permalink
Nested Parsers: Change architectures proposal...
Browse files Browse the repository at this point in the history
* Resolver will be passed to resolve method instead of parser returning
  a template.
* Parsing log message is returning an error which is defined by each
  type implementing LogMessage trait.
* Types with no errors sets the error to infallible for compiler
  optimizations.
* This approach enables saving parsing errors.
* Nested errors are ignored for now
  • Loading branch information
AmmarAbouZor committed Sep 9, 2024
1 parent 6df3299 commit f2733f2
Show file tree
Hide file tree
Showing 10 changed files with 254 additions and 199 deletions.
13 changes: 7 additions & 6 deletions application/apps/indexer/indexer_cli/src/interactive.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -49,7 +50,7 @@ pub(crate) async fn handle_interactive_session(input: Option<PathBuf>) {
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 {
Expand Down
43 changes: 30 additions & 13 deletions application/apps/indexer/parsers/src/dlt/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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}';
Expand Down Expand Up @@ -538,6 +541,8 @@ impl<'a> FormattableMessage<'a> {
}

impl LogMessage for FormattableMessage<'_> {
type ParseError = GeneralParseLogError;

fn to_writer<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
let bytes = self.message.as_bytes();
let len = bytes.len();
Expand All @@ -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<impl Display, Self::ParseError> {
let mut msg = String::new();
// Taken from Documentation: string formatting is considered an infallible operation.
// Thus we can ignore `fmt::Error` errors.
Expand Down Expand Up @@ -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)
}
}

Expand Down
20 changes: 15 additions & 5 deletions application/apps/indexer/parsers/src/dlt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand All @@ -39,27 +39,37 @@ 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<W: Write>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
writer.write_u64::<BigEndian>(self.range.start as u64)?;
writer.write_u64::<BigEndian>(self.range.end as u64)?;
Ok(8 + 8)
}

fn try_resolve(&self) -> crate::LogMessageContent {
self.into()
fn try_resolve(
&self,
_resolver: Option<&mut crate::nested_parser::ParseRestResolver>,
) -> Result<impl Display, Self::ParseError> {
Ok(self)
}
}

impl LogMessage for RawMessage {
type ParseError = Infallible;

fn to_writer<W: Write>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
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<impl Display, Self::ParseError> {
Ok(self)
}
}

Expand Down
172 changes: 79 additions & 93 deletions application/apps/indexer/parsers/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -87,116 +89,100 @@ pub enum MessageStreamItem<T: LogMessage> {
Done,
}

pub trait LogMessage: Serialize {
/// Serializes a message directly into a Writer
/// returns the size of the serialized message
fn to_writer<W: Write>(&self, writer: &mut W) -> Result<usize, std::io::Error>;

/// 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<TemplateLogMsgChunk>,
resolve_hints: Vec<ResolveParseHint>,
}
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<u8>),
}

impl<T> From<T> 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<TemplateLogMsgChunk>, resolve_hints: Vec<ResolveParseHint>) -> 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<ResolveParseHint> {
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<F>(&mut self, mut parse_fn: F) -> Option<String>
where
F: FnMut(&[u8]) -> Option<String>,
{
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<W: Write>(&self, writer: &mut W) -> Result<usize, std::io::Error>;

/// 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<impl Display, Self::ParseError>;
}

#[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,
}
Loading

0 comments on commit f2733f2

Please sign in to comment.