#![allow(unused)]
use std::collections::HashMap;
#[derive(Default)]
pub struct Pretree {
tree_group: HashMap<String, Tree>,
}
impl Pretree {
pub fn new() -> Pretree {
let mut p = Pretree::default();
let methods = [
"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "CONNECT", "OPTIONS", "TRACE",
];
for method in &methods {
let tree = Tree::new(method);
p.tree_group.insert(method.to_string(), tree);
}
p
}
pub fn store(&mut self, method: &str, url_rule: &str) {
let t = self.tree_group.get_mut(method).unwrap();
t.insert(url_rule);
}
pub fn query(&self, method: &str, url_path: &str) -> (bool, String, HashMap<String, String>) {
let t = self.tree_group.get(method).unwrap();
let (is_exist, node, vars) = t.search(url_path);
if is_exist {
(true, node.rule().into(), vars)
} else {
(false, "".to_string(), vars)
}
}
}
#[derive(Clone)]
struct Tree {
rule: String,
name: String,
nodes: Vec<Tree>,
is_end: bool,
is_variable: bool,
}
impl Tree {
fn new(name: &str) -> Tree {
Tree {
rule: String::from(""),
name: name.to_string(),
nodes: vec![],
is_end: false,
is_variable: false,
}
}
fn with_variable(name: &str, is_variable: bool) -> Tree {
Tree {
rule: String::from(""),
name: name.to_string(),
nodes: vec![],
is_end: false,
is_variable,
}
}
fn append_child(&mut self, node: Tree) {
self.nodes.push(node);
}
pub fn rule(&self) -> &str {
&self.rule
}
pub fn name(&self) -> &str {
&self.name
}
pub fn var_name(&self) -> String {
self.name.trim_start_matches(':').to_string()
}
fn insert(&mut self, url_rule: &str) {
let mut current = Some(self); let list = parse_path(url_rule);
for word in &list {
let now = current.take().unwrap();
let mut index = None;
for (idx, tree) in now.nodes.iter().enumerate() {
if tree.name() == word {
index = Some(idx);
break;
}
}
if let Some(i) = index {
current = now.nodes.get_mut(i);
} else {
let node = Tree::with_variable(word, is_variable(word));
now.append_child(node);
current = now.nodes.last_mut();
}
}
assert!(current.is_some());
if let Some(current) = current.take() {
current.rule = url_rule.into();
current.is_end = true;
}
}
fn search(&self, url_path: &str) -> (bool, &Tree, HashMap<String, String>) {
let mut vars: HashMap<String, String> = HashMap::new();
let mut current = Some(self);
let list = parse_path(url_path);
'for_list: for (index, word) in list.into_iter().enumerate() {
let now = current.take().unwrap();
for n in now.nodes.iter() {
if n.name() == word {
current.replace(n);
continue 'for_list;
}
}
for m in now.nodes.iter() {
if m.is_variable && index > 0 {
vars.insert(m.var_name(), word);
current.replace(m);
continue 'for_list;
}
}
current.replace(now);
}
let res = current.unwrap();
let is_end = res.is_end;
(is_end, res, vars)
}
}
fn parse_path(path: &str) -> Vec<String> {
let newpath = format_rule(path);
let split = newpath.split('/');
let paths: Vec<String> = split.map(|s| s.to_owned()).collect();
paths
}
fn format_rule(rule: &str) -> String {
rule.replace("{", ":").replace("}", "")
}
fn is_variable(s: &str) -> bool {
s.starts_with(':')
}
#[cfg(test)]
mod tests {
#[test]
fn test_match() {
use crate::Pretree;
let data: [[&str; 3]; 20] = [
["POST", "/pet/{petId}/uploadImage", "/pet/12121/uploadImage"],
["POST", "/pet", "/pet"],
["PUT", "/pet", "/pet"],
["GET", "/pet/findByStatus", "/pet/findByStatus"],
["GET", "/pet/{petId}", "/pet/113"],
["GET", "/pet/{petId}/info", "/pet/12121/info"],
["POST", "/pet/{petId}", "/pet/12121"],
["DELETE", "/pet/{petId}", "/pet/12121"],
["GET", "/store/inventory", "/store/inventory"],
["POST", "/store/order", "/store/order"],
["GET", "/store/order/{orderId}", "/store/order/939"],
["DELETE", "/store/order/{orderId}", "/store/order/939"],
["POST", "/user/createWithList", "/user/createWithList"],
["GET", "/user/{username}", "/user/1002"],
["PUT", "/user/{username}", "/user/1002"],
["DELETE", "/user/{username}", "/user/1002"],
["GET", "/user/login", "/user/login"],
["GET", "/user/logout", "/user/logout"],
["POST", "/user/createWithArray", "/user/createWithArray"],
["POST", "/user", "/user"],
];
println!("{:?}", data);
let mut p = Pretree::new();
for v in data {
let method = v[0];
let source_rule = v[1];
p.store(method, source_rule);
}
for v in data {
let method = v[0];
let url_path = v[2];
let source_rule = v[1];
let (ok, rule, _) = p.query(method, url_path);
println!("ok:{},rule: {} ", ok, rule,);
assert_eq!(ok, true);
assert_eq!(rule, source_rule);
}
}
}