tramex_tools/interface/
parse_config.rs1use std::fmt;
4
5#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
6pub enum Technology {
8 LTE,
10 NR,
12 #[default]
14 Unknown,
15}
16
17impl fmt::Display for Technology {
18 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
19 match self {
20 Technology::LTE => write!(f, "LTE (4G)"),
21 Technology::NR => write!(f, "NR (5G)"),
22 Technology::Unknown => write!(f, "--"),
23 }
24 }
25}
26
27#[derive(Debug, Clone, Default)]
28pub struct FileMetadata {
30 pub technology: Technology,
32
33 pub cell_info: Option<String>,
35
36 pub version: Option<String>,
38
39 pub started_on: Option<String>,
41
42 pub rotated_on: Option<String>,
44
45 pub pci: Option<u16>,
47
48 pub mode: Option<String>,
50
51 pub arfcn: Option<u32>,
53
54 pub io_mode: Option<String>,
56
57 pub ssb_info: Vec<String>,
59}
60
61impl FileMetadata {
62 pub fn parse_from_lines(lines: &[String]) -> Self {
64 let mut metadata = FileMetadata::default();
65
66 for line in lines {
67 let trimmed = line.trim();
68
69 if !trimmed.starts_with('#') {
71 break;
72 }
73
74 if trimmed.starts_with("# Cell") {
76 metadata.cell_info = Some(trimmed.to_string());
77
78 if trimmed.contains("nr_arfcn") {
80 metadata.technology = Technology::NR;
81 } else if trimmed.contains("earfcn") {
82 metadata.technology = Technology::LTE;
83 }
84
85 if let Some(pci_value) = Self::extract_value(trimmed, "pci=") {
87 metadata.pci = pci_value.parse().ok();
88 }
89
90 if let Some(mode_value) = Self::extract_value(trimmed, "mode=") {
92 metadata.mode = Some(mode_value.to_uppercase());
93 }
94
95 if let Some(arfcn_value) = Self::extract_value(trimmed, "nr_arfcn=") {
97 metadata.arfcn = arfcn_value.parse().ok();
98 } else if let Some(arfcn_value) = Self::extract_value(trimmed, "earfcn=") {
99 metadata.arfcn = arfcn_value.parse().ok();
100 }
101
102 if let Some(dl_mu_value) = Self::extract_value(trimmed, "dl_mu=")
104 && let Some(ul_mu_value) = Self::extract_value(trimmed, "ul_mu=")
105 {
106 let input = if dl_mu_value == "1" { "SI" } else { "MI" };
107 let output = if ul_mu_value == "1" { "SO" } else { "MO" };
108 metadata.io_mode = Some(format!("{}{}", input, output));
109 }
110 }
111
112 if trimmed.starts_with("# SSB:") {
114 metadata.ssb_info.push(trimmed.to_string());
115 }
116
117 if trimmed.starts_with("# lteenb version") || trimmed.starts_with("# mme version") {
119 metadata.version = Some(trimmed.trim_start_matches('#').trim().to_string());
120 }
121
122 if trimmed.starts_with("# Started on") {
124 metadata.started_on = Some(trimmed.trim_start_matches("# Started on").trim().to_string());
125 }
126
127 if trimmed.starts_with("# Rotated on") {
129 metadata.rotated_on = Some(trimmed.trim_start_matches("# Rotated on").trim().to_string());
130 }
131 }
132 metadata
133 }
134
135 fn extract_value<'a>(line: &'a str, key: &str) -> Option<&'a str> {
137 line.find(key).map(|start| {
138 let value_start = start + key.len();
139 let rest = &line[value_start..];
140 let end = rest.find(' ').unwrap_or(rest.len());
142 &rest[..end]
143 })
144 }
145
146 pub fn get_header_value(&self, key: &str) -> Option<String> {
148 match key {
149 "technology" => Some(self.technology.to_string()),
150 "version" => self.version.clone(),
151 "started_on" => self.started_on.clone(),
152 "rotated_on" => self.rotated_on.clone(),
153 "cell_info" => self.cell_info.clone(),
154 "pci" => self.pci.map(|v| v.to_string()),
155 "mode" => self.mode.clone(),
156 "arfcn" => self.arfcn.map(|v| v.to_string()),
157 "io_mode" => self.io_mode.clone(),
158 "ssb_info" => {
159 if self.ssb_info.is_empty() {
160 None
161 } else {
162 Some(self.ssb_info.join("; "))
163 }
164 }
165 _ => None,
166 }
167 }
168}