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 n_rb: Option<u16>,
56
57 pub io_mode: Option<String>,
59
60 pub ssb_info: Vec<String>,
62}
63
64impl FileMetadata {
65 pub fn parse_from_lines(lines: &[String]) -> Self {
67 let mut metadata = FileMetadata::default();
68
69 for line in lines {
70 let trimmed = line.trim();
72
73 if !trimmed.starts_with('#') {
75 break;
76 }
77
78 if trimmed.starts_with("# Cell") {
80 metadata.cell_info = Some(trimmed.to_string());
81
82 if trimmed.contains("nr_arfcn") {
84 metadata.technology = Technology::NR;
85 } else if trimmed.contains("earfcn") {
86 metadata.technology = Technology::LTE;
87 }
88
89 if let Some(pci_value) = Self::extract_value(trimmed, "pci=") {
91 metadata.pci = pci_value.parse().ok();
92 }
93
94 if let Some(mode_value) = Self::extract_value(trimmed, "mode=") {
96 metadata.mode = Some(mode_value.to_uppercase());
97 }
98
99 if let Some(arfcn_value) = Self::extract_value(trimmed, "nr_arfcn=") {
101 metadata.arfcn = arfcn_value.parse().ok();
102 } else if let Some(arfcn_value) = Self::extract_value(trimmed, "earfcn=") {
103 metadata.arfcn = arfcn_value.parse().ok();
104 }
105
106 if let Some(n_rb_value) = Self::extract_value(trimmed, "n_rb_dl=") {
108 metadata.n_rb = n_rb_value.parse().ok();
109 if let Some(n_rb_ul_value) = Self::extract_value(trimmed, "n_rb_ul=")
110 && n_rb_ul_value != n_rb_value
111 {
112 log::error!(
113 "n_rb_dl and n_rb_ul are different: {} and {}, this is not supported n_rb_dl has been used",
114 n_rb_value,
115 n_rb_ul_value
116 );
117 }
118 }
119
120 if let Some(dl_mu_value) = Self::extract_value(trimmed, "dl_mu=")
122 && let Some(ul_mu_value) = Self::extract_value(trimmed, "ul_mu=")
123 {
124 let input = if dl_mu_value == "1" { "SI" } else { "MI" };
125 let output = if ul_mu_value == "1" { "SO" } else { "MO" };
126 metadata.io_mode = Some(format!("{}{}", input, output));
127 }
128 }
129
130 if trimmed.starts_with("# SSB:") {
132 metadata.ssb_info.push(trimmed.to_string());
133 }
134
135 if trimmed.starts_with("# lteenb version") || trimmed.starts_with("# mme version") {
137 metadata.version = Some(trimmed.trim_start_matches('#').trim().to_string());
138 }
139
140 if trimmed.starts_with("# Started on") {
142 metadata.started_on = Some(trimmed.trim_start_matches("# Started on").trim().to_string());
143 }
144
145 if trimmed.starts_with("# Rotated on") {
147 metadata.rotated_on = Some(trimmed.trim_start_matches("# Rotated on").trim().to_string());
148 }
149 }
150 log::debug!("{:?}", metadata);
151 metadata
152 }
153
154 fn extract_value<'a>(line: &'a str, key: &str) -> Option<&'a str> {
156 line.find(key).map(|start| {
157 let value_start = start + key.len();
158 let rest = &line[value_start..];
159 let end = rest.find(' ').unwrap_or(rest.len());
161 &rest[..end]
162 })
163 }
164
165 pub fn get_header_value(&self, key: &str) -> Option<String> {
167 match key {
168 "technology" => Some(self.technology.to_string()),
169 "version" => self.version.clone(),
170 "started_on" => self.started_on.clone(),
171 "rotated_on" => self.rotated_on.clone(),
172 "cell_info" => self.cell_info.clone(),
173 "pci" => self.pci.map(|v| v.to_string()),
174 "mode" => self.mode.clone(),
175 "arfcn" => self.arfcn.map(|v| v.to_string()),
176 "io_mode" => self.io_mode.clone(),
177 "ssb_info" => {
178 if self.ssb_info.is_empty() {
179 None
180 } else {
181 Some(self.ssb_info.join("; "))
182 }
183 }
184 _ => None,
185 }
186 }
187}