#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ConstructForm {
Element,
Pair,
}
impl ConstructForm {
#[must_use]
pub fn label(self) -> &'static str {
match self {
ConstructForm::Element => "element",
ConstructForm::Pair => "key/value",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ConstructTarget {
Map,
Flags,
Weighted,
}
impl ConstructTarget {
pub const ALL: [ConstructTarget; 3] = [
ConstructTarget::Map,
ConstructTarget::Flags,
ConstructTarget::Weighted,
];
#[must_use]
pub fn type_name(self) -> &'static str {
match self {
ConstructTarget::Map => "Map",
ConstructTarget::Flags => "Flags",
ConstructTarget::Weighted => "Weighted",
}
}
#[must_use]
pub fn form(self) -> ConstructForm {
match self {
ConstructTarget::Flags => ConstructForm::Element,
ConstructTarget::Map | ConstructTarget::Weighted => ConstructForm::Pair,
}
}
#[must_use]
pub fn lookup(segments: &[String]) -> Option<ConstructTarget> {
let last = segments.last()?;
ConstructTarget::ALL
.into_iter()
.find(|t| t.type_name() == last)
}
}
#[cfg(test)]
mod tests {
use super::{ConstructForm, ConstructTarget};
fn path(segments: &[&str]) -> Vec<String> {
segments.iter().map(|s| (*s).to_string()).collect()
}
#[test]
fn every_registry_entry_is_reachable_by_its_own_name() {
for target in ConstructTarget::ALL {
assert_eq!(
ConstructTarget::lookup(&path(&[target.type_name()])),
Some(target),
"{} must resolve to itself",
target.type_name()
);
}
}
#[test]
fn a_qualified_spelling_resolves_to_the_same_entry() {
assert_eq!(
ConstructTarget::lookup(&path(&["std", "map", "Map"])),
Some(ConstructTarget::Map)
);
assert_eq!(
ConstructTarget::lookup(&path(&["std", "collections", "Weighted"])),
Some(ConstructTarget::Weighted)
);
}
#[test]
fn an_unregistered_name_falls_through_rather_than_erroring() {
assert_eq!(ConstructTarget::lookup(&path(&["Point"])), None);
assert_eq!(ConstructTarget::lookup(&path(&["Heap"])), None);
assert_eq!(ConstructTarget::lookup(&[]), None);
}
#[test]
fn each_entry_declares_the_form_the_spec_gives_it() {
assert_eq!(ConstructTarget::Map.form(), ConstructForm::Pair);
assert_eq!(ConstructTarget::Weighted.form(), ConstructForm::Pair);
assert_eq!(ConstructTarget::Flags.form(), ConstructForm::Element);
}
#[test]
fn type_names_are_unique_so_lookup_is_unambiguous() {
let mut names: Vec<&str> = ConstructTarget::ALL
.into_iter()
.map(ConstructTarget::type_name)
.collect();
names.sort_unstable();
let before = names.len();
names.dedup();
assert_eq!(before, names.len(), "registry type names must be unique");
}
}