use std::collections::HashSet;
use smol_str::SmolStr;
use crate::bib::ast;
use crate::bib::semantic::Model;
use crate::bib::semantic::entry::{Entry, StringDef, StringUse};
use crate::bib::syntax::{SyntaxKind, SyntaxNode};
pub(crate) const MONTH_MACROS: [&str; 12] = [
"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec",
];
pub fn build(root: &SyntaxNode) -> Model {
let mut model = Model::default();
for node in root.descendants() {
match node.kind() {
SyntaxKind::ENTRY => collect_entry(&node, &mut model),
SyntaxKind::STRING_ENTRY => collect_string(&node, &mut model),
_ => {}
}
}
resolve(&mut model);
model
}
fn collect_entry(entry: &SyntaxNode, model: &mut Model) {
if let Some((key, key_range)) = ast::cite_key(entry) {
let entry_type = ast::entry_type(entry).unwrap_or_default().to_lowercase();
model.entries.push(Entry {
entry_type: SmolStr::new(entry_type),
key: SmolStr::new(key),
key_range,
range: entry.text_range(),
duplicate: false,
});
}
collect_uses(entry, model);
}
fn collect_string(string_entry: &SyntaxNode, model: &mut Model) {
if let Some((name, range)) = ast::string_def_name(string_entry) {
model.string_defs.push(StringDef {
name: SmolStr::new(name.to_lowercase()),
range,
});
}
collect_uses(string_entry, model);
}
fn collect_uses(node: &SyntaxNode, model: &mut Model) {
for field in ast::fields(node) {
let Some(value) = ast::field_value(&field) else {
continue;
};
for (name, range) in ast::value_macro_uses(&value) {
model.string_uses.push(StringUse {
name: SmolStr::new(name.to_lowercase()),
range,
resolved: false,
});
}
}
}
fn resolve(model: &mut Model) {
let mut seen: HashSet<SmolStr> = HashSet::new();
for entry in &mut model.entries {
let folded = SmolStr::new(entry.key.to_lowercase());
if !seen.insert(folded) {
entry.duplicate = true;
}
}
let mut defined: HashSet<SmolStr> = model.string_defs.iter().map(|d| d.name.clone()).collect();
defined.extend(MONTH_MACROS.iter().map(|m| SmolStr::new(*m)));
for string_use in &mut model.string_uses {
string_use.resolved = defined.contains(&string_use.name);
}
}