tramex_tools/interface/interface_file/
utils_file.rs

1//! utils functions for file interface
2
3use std::str::FromStr;
4
5use crate::errors::ErrorCode;
6use crate::interface::parser::{FileParser, parsing_error_to_tramex_error};
7use crate::tramex_error;
8use crate::{
9    data::Trace,
10    errors::TramexError,
11    interface::{
12        layer::Layer,
13        parser::{
14            eof_error, parser_basic::BasicParser, parser_gtpu::GTPUParser, parser_nas::NASParser, parser_ngap::NGAPParser,
15            parser_phy::PHYParser, parser_rrc::RRCParser,
16        },
17    },
18};
19
20/// Function that parses one log
21/// # Arguments
22/// * `lines` - The lines to parse
23/// * `ix` - The index of the current line
24/// # Returns
25/// * `Result<Trace, TramexError>` - The parsed trace or an error
26/// # Note
27/// Should receive only one block instead of the full remaining text ?
28///
29/// # Errors
30///
31/// Fails on parsing failure
32pub fn parse_one_block(lines: &[String], ix: &mut usize) -> Result<Trace, TramexError> {
33    // no more lines to read
34    if lines.is_empty() {
35        return Err(eof_error(*ix as u64));
36    }
37
38    // 1. Block Boundary Detection
39    let mut start_line = 0;
40    let mut end_line = 0;
41    let mut should_stop = false;
42    for one_line in lines.iter() {
43        end_line += 1;
44        if one_line.starts_with('#') {
45            start_line += 1;
46            continue; // Skip comments
47        } else if one_line.starts_with(' ') || one_line.starts_with('\t') || one_line.trim().is_empty() {
48            continue; // Keep continuation lines
49        } else {
50            if should_stop {
51                end_line -= 1;
52                break;
53            }
54            should_stop = true;
55        }
56    }
57
58    if end_line == 1 && (lines[0].starts_with(' ') || lines[0].starts_with('\t') || lines[0].trim().is_empty()) {
59        return Err(eof_error(*ix as u64));
60    }
61
62    // 2. Extract lines to parse
63    let lines_to_parse = &lines[start_line..end_line];
64    let copy_ix = *ix + start_line;
65    *ix += end_line;
66
67    // 3. Parse the first line to determine the layer
68    match lines_to_parse.first() {
69        Some(first_line) => {
70            let parts: Vec<&str> = first_line.split_whitespace().collect();
71            if parts.is_empty() {
72                return Err(tramex_error!(
73                    format!("Not enough parts in the line {:?} (line {})", first_line, copy_ix as u64 + 1),
74                    ErrorCode::FileParsing
75                ));
76            }
77
78            // Determine layer from [LAYER] tag
79            let res_layer = Layer::from_str(parts[1].trim_start_matches('[').trim_end_matches(']'));
80
81            // 4. Parse the trace
82            match res_layer {
83                Ok(Layer::RRC) => RRCParser::parse(lines_to_parse),
84                Ok(Layer::NAS) => NASParser::parse(lines_to_parse),
85                Ok(Layer::NGAP) => NGAPParser::parse(lines_to_parse),
86                Ok(Layer::GTPU) => GTPUParser::parse(lines_to_parse),
87                Ok(Layer::PHY) => PHYParser::parse(lines_to_parse),
88                Ok(layer) => BasicParser::parse_with_layer(lines_to_parse, layer),
89                Err(_) => {
90                    return Err(tramex_error!(
91                        format!(
92                            "Unknown message type {:?} in {:?} (line {})",
93                            parts[1],
94                            first_line,
95                            copy_ix + 1
96                        ),
97                        ErrorCode::ParsingLayerNotImplemented
98                    ));
99                }
100            }
101            .map_err(|err| parsing_error_to_tramex_error(err, copy_ix as u64))
102        }
103        None => Err(eof_error(copy_ix as u64)),
104    }
105}