use std::collections::HashMap;
pub struct FrbConfig {
pub styles: HashMap<String, String>,
pub prefixes: HashMap<String, String>,
}
enum Section {
Styles(Option<String>),
Prefixes(Option<String>),
None,
}
pub enum ParseError {
InvalidSection(String),
InvalidKeyValue(String),
}
impl std::fmt::Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidSection(s) => write!(f, "invalid section: '{s}'"),
Self::InvalidKeyValue(s) => write!(f, "invalid key value: '{s}'"),
}
}
}
pub fn parse(input: &str) -> Result<FrbConfig, ParseError> {
let mut styles = HashMap::new();
let mut prefixes = HashMap::new();
let mut current_section = Section::None;
for line in input.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
if line.starts_with('[') {
let Some(closing) = line.find(']') else {
return Err(ParseError::InvalidSection(line.to_string()));
};
let section_str = &line[1..closing];
let mut parts = section_str.splitn(2, '.');
let section_name = parts.next().unwrap();
let namespace = parts.next().map(|s| s.to_string());
current_section = match section_name {
"styles" => Section::Styles(namespace),
"prefixes" => Section::Prefixes(namespace),
_ => return Err(ParseError::InvalidSection(section_str.to_string())),
};
} else if line.contains('=') {
let mut parts = line.splitn(2, '=');
let key = parts.next().unwrap().trim();
let value = parts
.next()
.ok_or(ParseError::InvalidKeyValue(line.to_string()))?
.trim()
.strip_prefix('"')
.and_then(|s| s.strip_suffix('"'))
.ok_or(ParseError::InvalidKeyValue(line.to_string()))?;
let full_key = match ¤t_section {
Section::Styles(Some(ns)) | Section::Prefixes(Some(ns)) => format!("{ns}:{key}"),
_ => key.to_string(),
};
match ¤t_section {
Section::Styles(_) => styles.insert(full_key, value.to_string()),
Section::Prefixes(_) => prefixes.insert(full_key, value.to_string()),
Section::None => return Err(ParseError::InvalidKeyValue(line.to_string())),
};
}
}
Ok(FrbConfig { styles, prefixes })
}