use regex::Regex;
use std::cell::RefCell;
use std::collections::{HashMap, hash_map};
#[derive(Debug,PartialEq,Eq,Hash)]
pub enum SplitAddr<'a> {
Term(&'a str),
Prefix(&'a str, &'a str)
}
use SplitAddr::{Prefix,Term};
thread_local!(
static ADDR_RE: RefCell<Regex> = RefCell::new(Regex::new(r"^(.*?)/(.*)$").ok().unwrap())
);
impl<'a> SplitAddr<'a> {
pub fn from_addr(addr: &'a str) -> Self {
match ADDR_RE.with(|re| re.borrow().captures(&addr)) {
None => {
Term(addr.trim_start().trim_end())
},
Some(caps) => {
let first: &str = caps.get(1).unwrap().into();
let rest: &str = caps.get(2).unwrap().into();
Prefix(first.trim_start().trim_end(), rest)
}
}
}
}
pub fn normalize_addr<'a>(addr: &'a str) -> String {
match SplitAddr::from_addr(addr) {
Term(s) => {
s.to_string()
}
Prefix(first, rest) => {
format!("{} / {}", first, normalize_addr(rest))
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct AddrMap(HashMap<String,AddrMap>);
impl AddrMap {
pub fn new() -> Self {
AddrMap(HashMap::new())
}
pub fn is_leaf(&self) -> bool {
self.0.is_empty()
}
pub fn search(&self, addr: &str) -> Option<&AddrMap> {
match SplitAddr::from_addr(addr) {
Term(addr) => {
self.0.get(addr)
}
Prefix(first, rest) => {
match self.0.get(first) {
Some(submask) => {
submask.search(rest)
}
None => { None }
}
}
}
}
pub fn insert(&mut self, addr: &str, sub: AddrMap) {
self.0.insert(addr.to_string(), sub);
}
pub fn all_visited(&self, other: &AddrMap) -> bool {
for (addr, sub) in other.iter() {
if let Some(subvisitor) = self.search(&addr) {
if !subvisitor.is_leaf() && !subvisitor.all_visited(sub) {
return false;
}
} else {
return false;
}
}
return true;
}
pub fn visit(&mut self, addr: &str) {
match SplitAddr::from_addr(addr) {
Term(addr) => {
self.0
.entry(addr.to_string())
.or_insert(AddrMap::new());
}
Prefix(first, rest) => {
let submask = self.0
.entry(first.to_string())
.or_insert(AddrMap::new());
submask.visit(rest);
}
}
}
pub fn complement(&self, mask: &Self) -> Self {
let mut cmap = AddrMap::new();
for (addr, sub) in self.iter() {
match mask.search(addr) {
None => {
cmap.visit(addr);
}
Some(submask) => {
if !sub.is_leaf() && !submask.is_leaf() {
let subcomplement = sub.complement(submask);
if !subcomplement.is_leaf() {
cmap.insert(addr, subcomplement);
}
}
}
}
}
cmap
}
pub fn iter(&self) -> hash_map::Iter<'_, String, AddrMap> {
self.0.iter()
}
}
#[test]
fn test_split_addr() {
let key = SplitAddr::from_addr("test");
assert_eq!(key, Term("test"));
let key = SplitAddr::from_addr("(tuple, test)");
assert_eq!(key, Term("(tuple, test)"));
let key = SplitAddr::from_addr("1/2");
assert_eq!(key, Prefix("1", "2"));
let hard_addr = " 1/ 21f23/432 / 132 / ( y?A1 , grexxy ) ";
let mut key = SplitAddr::from_addr(hard_addr);
assert_eq!(key, Prefix("1", " 21f23/432 / 132 / ( y?A1 , grexxy ) "));
while key != Term("( y?A1 , grexxy )") {
match key {
Prefix(_, b) => { key = SplitAddr::from_addr(b); },
t => { panic!("expected term = Term(\"( y?A1 , grexxy )\"), got {:?}", t) }
}
}
let equiv_addr = "1/ 21f23 / 432/132 / ( y?A1 , grexxy ) ";
let normalized_addr = "1 / 21f23 / 432 / 132 / ( y?A1 , grexxy )";
assert_eq!(normalize_addr(hard_addr), normalized_addr);
assert_eq!(normalize_addr(equiv_addr), normalized_addr);
}