use crate::error::MatrixError;
pub struct ColDef {
pub pos: usize,
pub is_num: bool,
}
pub fn parse_marker(line: &str) -> Result<Vec<ColDef>, MatrixError> {
let mut defs = Vec::new();
for (idx, ch) in line.chars().enumerate() {
match ch {
'>' => defs.push(ColDef {
pos: idx,
is_num: true,
}),
'|' | '_' => defs.push(ColDef {
pos: idx,
is_num: false,
}),
_ => {}
}
}
if defs.is_empty() {
return Err(MatrixError::InvalidFormat("No column markers".into()));
}
Ok(defs)
}
pub fn slice_numbers(line: &str, defs: &[ColDef]) -> Result<Vec<f64>, MatrixError> {
let chars: Vec<char> = line.chars().collect();
let mut nums = Vec::new();
for (i, d) in defs.iter().enumerate() {
if !d.is_num {
continue;
}
let end = if i + 1 < defs.len() {
defs[i + 1].pos
} else {
chars.len()
};
if d.pos >= chars.len() {
continue;
}
let s: String = chars[d.pos..end.min(chars.len())].iter().collect();
let s = s.trim();
if s.is_empty() {
continue;
}
match s.parse::<f64>() {
Ok(n) => nums.push(n),
Err(_) => {
return Err(MatrixError::InvalidFormat(format!(
"expected number at col {}, got '{}'",
i, s
)));
}
}
}
Ok(nums)
}
pub fn slice_all(line: &str, defs: &[ColDef]) -> Vec<String> {
let chars: Vec<char> = line.chars().collect();
let mut result = Vec::new();
for (i, d) in defs.iter().enumerate() {
let end = if i + 1 < defs.len() {
defs[i + 1].pos
} else {
chars.len()
};
if d.pos >= chars.len() {
result.push(String::new());
} else {
let s: String = chars[d.pos..end.min(chars.len())].iter().collect();
result.push(s.trim().to_string());
}
}
result
}
pub fn slice_op(slices: &[String], defs: &[ColDef]) -> String {
for (i, d) in defs.iter().enumerate() {
if !d.is_num {
return slices.get(i).cloned().unwrap_or_default();
}
}
String::new()
}
pub fn col_widths(defs: &[ColDef]) -> Vec<usize> {
let mut ws = vec![];
for i in 0..defs.len() {
let end = if i + 1 < defs.len() {
defs[i + 1].pos
} else {
defs[i].pos + 6
};
ws.push(end.saturating_sub(defs[i].pos));
}
ws
}