use rubydex::model::{
graph::Graph,
ids::NameId,
name::{Name, ParentScope},
};
pub fn nesting_stack_to_name_id(
graph: &mut Graph,
const_name: &str,
nesting: Vec<String>,
) -> Option<(NameId, Vec<NameId>)> {
let mut current_nesting = None;
let mut current_name = ParentScope::None;
let mut names_to_untrack = Vec::new();
for entry in nesting {
process_qualified_name(
graph,
&entry,
&mut current_name,
&mut current_nesting,
&mut names_to_untrack,
);
current_nesting = current_name.as_ref().copied();
current_name = ParentScope::None;
}
process_qualified_name(
graph,
const_name,
&mut current_name,
&mut current_nesting,
&mut names_to_untrack,
);
let (ParentScope::Some(name_id) | ParentScope::Attached(name_id)) = current_name else {
return None;
};
Some((name_id, names_to_untrack))
}
fn process_qualified_name(
graph: &mut Graph,
qualified_name: &str,
current_name: &mut ParentScope,
current_nesting: &mut Option<NameId>,
names_to_untrack: &mut Vec<NameId>,
) {
for part in qualified_name.split("::") {
if part.is_empty() {
*current_name = ParentScope::TopLevel;
continue;
}
let (parent_scope, nesting_for_part) = if part.starts_with('<') {
let attached_id = match *current_name {
ParentScope::Some(id) | ParentScope::Attached(id) => Some(id),
_ => *current_nesting,
};
let attached = attached_id.map_or(ParentScope::None, ParentScope::Attached);
(attached, attached_id)
} else {
(*current_name, *current_nesting)
};
let str_id = graph.intern_string(part.to_owned());
let name_id = graph.add_name(Name::new(str_id, parent_scope, nesting_for_part));
names_to_untrack.push(name_id);
*current_name = ParentScope::Some(name_id);
}
}
#[cfg(test)]
mod tests {
use rubydex::model::ids::StringId;
use super::*;
#[test]
fn nesting_is_converted_to_name_id() {
let mut graph = Graph::new();
let (name_id, _) = nesting_stack_to_name_id(
&mut graph,
"Some::CONST",
vec!["Foo".into(), "Bar::Zip".into(), "Qux".into()],
)
.unwrap();
let const_name = graph.names().get(&name_id).unwrap();
assert_eq!(StringId::from("CONST"), *const_name.str());
let some_name = graph
.names()
.get(&const_name.parent_scope().expect("Parent scope should exist"))
.unwrap();
assert_eq!(StringId::from("Some"), *some_name.str());
assert_eq!(const_name.nesting(), some_name.nesting());
let qux_name = graph.names().get(&some_name.nesting().unwrap()).unwrap();
assert_eq!(StringId::from("Qux"), *qux_name.str());
assert!(qux_name.parent_scope().is_none());
let zip_name = graph.names().get(&qux_name.nesting().unwrap()).unwrap();
assert_eq!(StringId::from("Zip"), *zip_name.str());
let bar_name = graph
.names()
.get(&zip_name.parent_scope().expect("Parent scope should exist"))
.unwrap();
assert_eq!(StringId::from("Bar"), *bar_name.str());
assert_eq!(zip_name.nesting(), bar_name.nesting());
let foo_name = graph.names().get(&bar_name.nesting().unwrap()).unwrap();
assert_eq!(StringId::from("Foo"), *foo_name.str());
assert!(foo_name.parent_scope().is_none());
assert!(foo_name.nesting().is_none());
}
#[test]
fn top_level_reference_is_converted_to_name_id() {
let mut graph = Graph::new();
let (name_id, _) = nesting_stack_to_name_id(&mut graph, "::CONST", vec!["Foo".into()]).unwrap();
let const_name = graph.names().get(&name_id).unwrap();
assert_eq!(StringId::from("CONST"), *const_name.str());
assert!(const_name.parent_scope().is_top_level());
let foo_name = graph.names().get(&const_name.nesting().unwrap()).unwrap();
assert_eq!(StringId::from("Foo"), *foo_name.str());
assert!(foo_name.nesting().is_none());
assert!(foo_name.parent_scope().is_none());
}
#[test]
fn singleton_class_names_use_attached_parent_scope() {
let mut graph = Graph::new();
let (name_id, _) = nesting_stack_to_name_id(&mut graph, "CONST", vec!["Foo".into(), "<Foo>".into()]).unwrap();
let const_name = graph.names().get(&name_id).unwrap();
assert_eq!(StringId::from("CONST"), *const_name.str());
let singleton_name = graph.names().get(&const_name.nesting().unwrap()).unwrap();
assert_eq!(StringId::from("<Foo>"), *singleton_name.str());
assert!(
matches!(singleton_name.parent_scope(), ParentScope::Attached(_)),
"Expected ParentScope::Attached, got {}",
singleton_name.parent_scope()
);
let foo_id = singleton_name.parent_scope().expect("Attached should have an id");
let foo_name = graph.names().get(&foo_id).unwrap();
assert_eq!(StringId::from("Foo"), *foo_name.str());
}
#[test]
fn top_level_nesting_is_converted_to_name_id() {
let mut graph = Graph::new();
let (name_id, _) = nesting_stack_to_name_id(&mut graph, "CONST", vec!["Foo".into(), "::Bar".into()]).unwrap();
let const_name = graph.names().get(&name_id).unwrap();
assert_eq!(StringId::from("CONST"), *const_name.str());
assert!(const_name.parent_scope().is_none());
let bar_name = graph.names().get(&const_name.nesting().unwrap()).unwrap();
assert_eq!(StringId::from("Bar"), *bar_name.str());
assert!(bar_name.parent_scope().is_top_level());
let foo_name = graph.names().get(&bar_name.nesting().unwrap()).unwrap();
assert_eq!(StringId::from("Foo"), *foo_name.str());
assert!(foo_name.parent_scope().is_none());
assert!(foo_name.nesting().is_none());
}
}