use std::collections::HashMap;
use std::rc::Rc;
use crate::CommandDefinition;
#[derive(Debug)]
pub enum Error {
CommandExists { path: String },
QueryExists { path: String },
}
impl std::error::Error for Error {}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::CommandExists { path } => write!(f, "Command {} is already defined", path),
Error::QueryExists { path } => write!(f, "Query {} is already defined", path),
}
}
}
type NodeId = usize;
pub struct Tree {
pub items: HashMap<NodeId, TreeNode>,
}
#[derive(Default)]
pub struct TreeNode {
pub children: HashMap<String, NodeId>,
pub command: Option<Rc<CommandDefinition>>,
pub query: Option<Rc<CommandDefinition>>,
}
impl Tree {
pub fn new() -> Tree {
Tree {
items: HashMap::from([(0, TreeNode::default())]),
}
}
pub fn insert(&mut self, cmd: Rc<CommandDefinition>) -> Result<(), Error> {
cmd.command
.paths()
.iter()
.try_for_each(|path| self.insert_at(0, path, cmd.clone()))
}
fn insert_at(
&mut self,
id: NodeId,
path: &[String],
cmd: Rc<CommandDefinition>,
) -> Result<(), Error> {
use std::collections::hash_map::Entry;
if let Some(part) = path.first() {
let next_id = self.items.len();
let entry = self
.items
.get_mut(&id)
.expect("Node ID must exist in the tree")
.children
.entry(part.clone());
let node_id = match entry {
Entry::Occupied(o) => *o.get(),
Entry::Vacant(v) => {
v.insert(next_id);
next_id
}
};
if node_id == next_id {
self.items.insert(next_id, TreeNode::default());
}
self.insert_at(node_id, &path[1..], cmd)?;
} else {
let node = self.items.get_mut(&id).expect("Node ID must exist in the tree");
if cmd.command.is_query() {
if let Some(existing) = &node.query {
return Err(Error::QueryExists {
path: existing.command.canonical_path(),
});
} else {
node.query = Some(cmd)
}
} else {
if let Some(existing) = &node.command {
return Err(Error::CommandExists {
path: existing.command.canonical_path(),
});
} else {
node.command = Some(cmd)
}
}
}
Ok(())
}
}