use std::{collections::HashMap, sync::Arc};
use tairitsu_vdom::VNode;
use super::{Params, RouteHandler, RouteSegment};
pub struct TrieNode {
children: HashMap<String, Arc<TrieNode>>,
dynamic_child: Option<Arc<TrieNode>>,
wildcard_child: Option<Arc<TrieNode>>,
handler: Option<RouteHandler>,
name: Option<String>,
}
impl TrieNode {
pub fn new() -> Self {
Self {
children: HashMap::new(),
dynamic_child: None,
wildcard_child: None,
handler: None,
name: None,
}
}
pub fn insert(&self, segments: Vec<RouteSegment>, handler: RouteHandler) {
self.insert_named(segments, None, handler);
}
pub fn insert_named(
&self,
segments: Vec<RouteSegment>,
name: String,
handler: RouteHandler,
) {
if segments.is_empty() {
return;
}
let first = &segments[0];
let rest = &segments[1..];
match first.segment_type() {
super::SegmentType::Static => {
if let Some(child) = self.children.get(&first.to_string()) {
child.insert_named(rest.to_vec(), name, handler);
} else {
let new_node = Arc::new(TrieNode::new());
new_node.insert_named(rest.to_vec(), name, handler);
}
}
super::SegmentType::Dynamic => {
if let Some(child) = &self.dynamic_child {
child.insert_named(rest.to_vec(), name, handler);
} else {
let new_node = Arc::new(TrieNode::new());
new_node.insert_named(rest.to_vec(), name, handler);
}
}
super::SegmentType::Wildcard => {
if let Some(child) = &self.wildcard_child {
child.insert_named(rest.to_vec(), name, handler);
} else {
let new_node = Arc::new(TrieNode::new());
new_node.insert_named(rest.to_vec(), name, handler);
}
}
}
}
pub fn find(&self, segments: &[RouteSegment]) -> Option<(RouteHandler, Params, Option<String>)> {
self.find_helper(segments, &mut Params::new())
}
fn find_helper(
&self,
segments: &[RouteSegment],
params: &mut Params,
) -> Option<(RouteHandler, Params, Option<String>)> {
if segments.is_empty() {
if let Some(handler) = &self.handler {
return Some((handler.clone(), params.clone(), self.name.clone()));
}
return None;
}
let first = &segments[0];
let rest = &segments[1..];
if let Some(child) = self.children.get(&first.to_string()) {
if let Some(result) = child.find_helper(rest, params) {
return Some(result);
}
}
if let Some(child) = &self.dynamic_child {
if let Some(param_name) = first.param_name() {
params.insert(param_name.to_string(), first.to_string());
if let Some(result) = child.find_helper(rest, params) {
return Some(result);
}
params.remove(param_name);
}
}
if let Some(child) = &self.wildcard_child {
if let Some(result) = child.find_helper(rest, params) {
return Some(result);
}
}
None
}
pub fn find_prefix(
&self,
segments: &[RouteSegment],
) -> Option<(RouteHandler, Params, Option<String>)> {
self.find_prefix_helper(segments, &mut Params::new(), 0)
}
fn find_prefix_helper(
&self,
segments: &[RouteSegment],
params: &mut Params,
depth: usize,
) -> Option<(RouteHandler, Params, Option<String>)> {
if let Some(handler) = &self.handler {
return Some((
handler.clone(),
params.clone(),
self.name.clone(),
));
}
if segments.is_empty() {
return None;
}
let first = &segments[0];
let rest = &segments[1..];
if let Some(child) = self.children.get(&first.to_string()) {
if let Some(result) = child.find_prefix_helper(rest, params, depth + 1) {
return Some(result);
}
}
if let Some(child) = &self.dynamic_child {
if let Some(param_name) = first.param_name() {
params.insert(param_name.to_string(), first.to_string());
if let Some(result) = child.find_prefix_helper(rest, params, depth + 1) {
return Some(result);
}
params.remove(param_name);
}
}
if let Some(child) = &self.wildcard_child {
if let Some(result) = child.find_prefix_helper(rest, params, depth + 1) {
return Some(result);
}
}
None
}
pub fn url_for(&self, name: &str, params: &[(&str, &str)]) -> Option<String> {
self.url_for_helper(name, params, &mut Vec::new())
}
fn url_for_helper(
&self,
name: &str,
params: &[(&str, &str)],
segments: &mut Vec<String>,
) -> Option<String> {
if self.name.as_deref() == Some(name) {
let mut url = if segments.is_empty() {
"/".to_string()
} else {
format!("/{}", segments.join("/"))
};
return Some(url);
}
for (key, child) in &self.children {
let mut new_segments = segments.clone();
new_segments.push(key.clone());
if let Some(url) = child.url_for_helper(name, params, &mut new_segments) {
return Some(url);
}
}
None
}
}
impl Default for TrieNode {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn mock_handler(_params: Params) -> VNode {
VNode::Text(tairitsu_vdom::VText::new("mock"))
}
#[test]
fn test_trie_creation() {
let node = TrieNode::new();
assert!(node.children.is_empty());
assert!(node.dynamic_child.is_none());
assert!(node.wildcard_child.is_none());
}
#[test]
fn test_find_empty() {
let node = TrieNode::new();
let segments = RouteSegment::parse_path("/test");
assert!(node.find(&segments).is_none());
}
#[test]
fn test_find_prefix_empty() {
let node = TrieNode::new();
let segments = RouteSegment::parse_path("/test");
assert!(node.find_prefix(&segments).is_none());
}
}