use once_cell::sync::Lazy;
use regex::Regex;
#[derive(Debug, Clone)]
pub struct ParsedHeaderLine {
pub mnemonic: String,
pub unit: String,
pub value: String,
pub descr: String,
}
static HEADER_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"^\s*([^.]*?)\.(\S*)\s+(.*?)\s+:\s*(.*)$").unwrap()
});
static HEADER_COMPACT_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"^\s*([^.]*?)\.(\S*?)\s*:\s*(.*)$").unwrap()
});
static NO_COLON_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"^\s*([^.]*?)\.(\S*)\s*(.*)$").unwrap()
});
pub fn parse_header_line(line: &str) -> Option<ParsedHeaderLine> {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with('#') {
return None;
}
if !trimmed.contains('.') {
return parse_no_period_line(trimmed);
}
if let Some(caps) = HEADER_RE.captures(trimmed) {
let mnemonic = caps.get(1).map_or("", |m| m.as_str()).trim().to_string();
if !mnemonic.contains(':') {
let unit = strip_brackets(caps.get(2).map_or("", |m| m.as_str()).trim());
let value = caps.get(3).map_or("", |m| m.as_str()).trim().to_string();
let descr = caps.get(4).map_or("", |m| m.as_str()).trim().to_string();
return Some(ParsedHeaderLine { mnemonic, unit, value, descr });
}
return parse_no_period_line(trimmed);
}
if let Some(caps) = HEADER_COMPACT_RE.captures(trimmed) {
let mnemonic = caps.get(1).map_or("", |m| m.as_str()).trim().to_string();
if !mnemonic.contains(':') {
let unit = strip_brackets(caps.get(2).map_or("", |m| m.as_str()).trim());
let descr = caps.get(3).map_or("", |m| m.as_str()).trim().to_string();
return Some(ParsedHeaderLine { mnemonic, unit, value: String::new(), descr });
}
return parse_no_period_line(trimmed);
}
if let Some(caps) = NO_COLON_RE.captures(trimmed) {
let mnemonic = caps.get(1).map_or("", |m| m.as_str()).trim().to_string();
if !mnemonic.contains(':') {
let unit = strip_brackets(caps.get(2).map_or("", |m| m.as_str()).trim());
let value = caps.get(3).map_or("", |m| m.as_str()).trim().to_string();
return Some(ParsedHeaderLine { mnemonic, unit, value, descr: String::new() });
}
return parse_no_period_line(trimmed);
}
None
}
fn parse_no_period_line(trimmed: &str) -> Option<ParsedHeaderLine> {
if let Some(sep_pos) = trimmed.rfind(" : ") {
let before = trimmed[..sep_pos].trim();
let after = trimmed[sep_pos+3..].trim();
if !before.is_empty() {
return Some(ParsedHeaderLine {
mnemonic: before.to_string(),
unit: String::new(),
value: after.to_string(),
descr: String::new(),
});
}
}
if let Some(colon_pos) = trimmed.find(':') {
let before = trimmed[..colon_pos].trim();
let after = trimmed[colon_pos+1..].trim();
if !before.is_empty() {
return Some(ParsedHeaderLine {
mnemonic: before.to_string(),
unit: String::new(),
value: after.to_string(),
descr: String::new(),
});
}
}
None
}
fn strip_brackets(unit: &str) -> String {
let s = unit.trim();
if (s.starts_with('[') && s.ends_with(']'))
|| (s.starts_with('(') && s.ends_with(')'))
{
s[1..s.len()-1].to_string()
} else {
s.to_string()
}
}
pub fn parse_value(value_str: &str, mnemonic: &str) -> ValueType {
let upper = mnemonic.to_uppercase();
if upper == "UWI" || upper == "API" {
return ValueType::Str(value_str.to_string());
}
let trimmed = value_str.trim();
if trimmed.is_empty() {
return ValueType::Str(String::new());
}
if let Ok(i) = trimmed.parse::<i64>() {
if !trimmed.contains('.') {
return ValueType::Int(i);
}
}
if let Ok(f) = trimmed.parse::<f64>() {
return ValueType::Float(f);
}
ValueType::Str(value_str.to_string())
}
#[derive(Debug, Clone)]
pub enum ValueType {
Str(String),
Int(i64),
Float(f64),
}