use super::MixedKeyword;
pub fn parse_float_with_comma_decimal(value: &str) -> Option<f32> {
if let Ok(val) = value.trim().parse::<f32>() {
return Some(val);
}
let normalized = value.trim().replace(',', ".");
normalized.parse::<f32>().ok()
}
pub fn parse_float_tuple(value: &str) -> Option<(f32, f32)> {
let parts: Vec<&str> = value.trim().split(',').collect();
if parts.len() == 2 {
let f1 = parse_float_with_comma_decimal(parts[0])?;
let f2 = parse_float_with_comma_decimal(parts[1])?;
Some((f1, f2))
}
else if parts.len() == 4 {
let f1_str = format!("{}.{}", parts[0], parts[1]);
let f2_str = format!("{}.{}", parts[2], parts[3]);
let f1 = f1_str.parse::<f32>().ok()?;
let f2 = f2_str.parse::<f32>().ok()?;
Some((f1, f2))
} else {
None
}
}
pub fn parse_float_vector(value: &str) -> Option<Vec<f32>> {
value
.trim()
.split(',')
.map(parse_float_with_comma_decimal)
.collect()
}
pub fn validate_pnd_scale_type(scale_type: &str) -> bool {
matches!(scale_type.trim(), "Linear" | "Logarithmic")
}
pub fn parse_pnd(value: &str) -> Option<MixedKeyword> {
let parts: Vec<&str> = value.trim().split(',').collect();
if parts.len() == 3 {
let scale_type = parts[0].trim().to_string();
if !validate_pnd_scale_type(&scale_type) {
return None;
}
let f1 = parse_float_with_comma_decimal(parts[1])?;
let f2 = parse_float_with_comma_decimal(parts[2])?;
Some(MixedKeyword::PnD(scale_type, f1, f2))
} else {
None
}
}
pub fn parse_spillover(value: &str) -> Option<MixedKeyword> {
let parts: Vec<&str> = value.trim().split(',').collect();
if parts.is_empty() {
return None;
}
let n_parameters = parts[0].trim().parse::<usize>().ok()?;
if parts.len() < 1 + n_parameters {
return None; }
let parameter_names: Vec<String> = parts[1..=n_parameters]
.iter()
.map(|s| s.trim().to_string())
.collect();
let expected_matrix_size = n_parameters * n_parameters;
let matrix_start = 1 + n_parameters;
if parts.len() < matrix_start + expected_matrix_size {
return None; }
let matrix_values: Option<Vec<f32>> = parts[matrix_start..matrix_start + expected_matrix_size]
.iter()
.map(|s| parse_float_with_comma_decimal(s))
.collect();
matrix_values.map(|matrix_values| MixedKeyword::SPILLOVER {
n_parameters,
parameter_names,
matrix_values,
})
}
const PARAMETER_KEYWORD_PREFIXES: &[&str] = &["P", "G", "R"];
pub fn extract_parameter_suffix(key: &str) -> Option<String> {
let rest = if let Some(rest) = key.strip_prefix('P') {
rest
} else if let Some(rest) = key.strip_prefix('G') {
rest
} else if let Some(rest) = key.strip_prefix('R') {
rest
} else {
return None;
};
let numeric_end = rest
.char_indices()
.find(|(_, c)| !c.is_numeric())
.map(|(idx, _)| idx);
if let Some(end_idx) = numeric_end {
let suffix = rest[end_idx..].to_string();
if suffix.is_empty() {
None
} else {
Some(suffix)
}
} else {
None
}
}
pub fn is_parameter_keyword(key: &str) -> bool {
for prefix in PARAMETER_KEYWORD_PREFIXES {
if let Some(rest) = key.strip_prefix(prefix) {
if rest.chars().next().map_or(false, |c| c.is_numeric()) {
let has_suffix = rest.chars().skip_while(|c| c.is_numeric()).next().is_some();
if has_suffix {
return true;
}
}
}
}
false
}