fr-trie 0.0.4

Fuzzy Radix Trie
Documentation
//! A specific Trie implementation for Access Control Lists supporting (limited) glob matching
use std::fmt::{Display, Formatter};
use std::ops::{BitOr, BitOrAssign};
use std::sync::Arc;
use bitflags::bitflags;
use serde::{Serialize, Deserialize};
use crate::trie::Trie;
use crate::key::{KeyPrefix, ValueMerge};
use crate::matcher::{MatchType, StateSequence};

#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub struct Acl {
    pub path: String,
}

impl Acl {
    pub fn new(path: &str) -> Self {
        Self {
            path: path.to_string()
        }
    }
}

///////////////////////////

impl Display for Acl {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_fmt(format_args!("{}", self.path))
    }
}

impl KeyPrefix for Acl {

    #[inline]
    fn key_chars(&self) -> Vec<char> {
        self.path.chars().collect::<Vec<_>>()
    }

    #[inline]
    fn key_len(&self) -> usize {
        self.path.len()
    }

    #[inline]
    fn empty() -> Self {
        Self {
            path: String::new(),
        }
    }

    #[inline]
    fn new_from_key_prefix(&self, index: usize) -> Self {
        Self {
            path: self.path[..index].to_string()
        }
    }

    #[inline]
    fn new_from_postfix(&self, index: usize) -> Self {
        Self {
            path: self.path[index..].to_string()
        }
    }

    #[inline]
    fn compiled(&self) -> Vec<Arc<StateSequence>> {
        let mut compiled_seq = Vec::new();
        let mut buff: Vec<char> = Vec::new();
        let mut next_state: MatchType = MatchType::Literal;
        for ch in self.path.chars() {
            if ch == '*' {
                if buff.len() > 0 {
                    compiled_seq.push(Arc::new(StateSequence {
                        match_type: next_state.clone(),
                        sequence: buff,
                    }));
                    next_state = MatchType::AnyOr;
                }
                else {
                    next_state = MatchType::AnyOr;
                }
                buff = Vec::new();
            }
            else {
                buff.push(ch);
            }
        }
        if !buff.is_empty() {
            compiled_seq.push(Arc::new(StateSequence {
                match_type: next_state,
                sequence: buff,
            }));
        }
        else if next_state == MatchType::AnyOr {
            compiled_seq.push(Arc::new(StateSequence {
                match_type: next_state,
                sequence: Vec::new(),
            }));
        }
        compiled_seq
    }

}

///////////////////
bitflags! {
    #[derive(Default, Serialize, Deserialize)]
    pub struct Permissions: u8 {
        const READ        = 0b00000001;
        const WRITE       = 0b00000010;
        const CREATE      = 0b00000100;
        const DELETE      = 0b00001000;
        const WATCH       = 0b00010000;
        const RESERVED_1  = 0b00100000;
        const RESERVED_2  = 0b01000000;
        const OWNER       = 0b10000000 | Self::READ.bits | Self::WRITE.bits | Self::CREATE.bits | Self::DELETE.bits | Self::WATCH.bits;
    }
}

impl ValueMerge for Permissions {
    fn merge(&self, other: &Self) -> Self {
        self.bitor(*other)
    }

    fn merge_mut(&mut self, other: &Self) {
        self.bitor_assign(*other)
    }
}

pub type AclTrie = Trie<Acl,Permissions>;

#[cfg(test)]
mod tests {

    use crate::key::TrieKey;
    use crate::matcher::{Event, PushdownStateMachine};
    use crate::glob::GlobMatcher;
    use crate::glob::acl::*;


    #[test]
    fn acl_trie_test() {
        {
            let token1 = String::from("f20000000001XXXG");
            let acl1 = TrieKey::new(Acl::new("f20*1*F"));
            println!("Processing {:?}", token1);
            let mut machine = GlobMatcher::new();

            machine.step_in(&acl1.seq);
            for ch in token1.chars() {
                machine.feed(Event::CharIn(ch));
                if machine.is_sink() {
                    break;
                }
            }

            println!("Machine is at {:?} state", machine.state());

            let token2 = String::from("T");

            let acl2 = TrieKey::new(Acl::new("T"));
            machine.step_in(&acl2.seq);
            for ch in token2.chars() {
                machine.feed(Event::CharIn(ch));
                if machine.is_sink() {
                    break;
                }
            }
            machine.feed(Event::EndOfStream);
            println!("Machine stopped at {:?} state", machine.state());
            machine.step_out();
        }

        {
            let acl = vec![TrieKey::new(Acl::new("f1*t")), TrieKey::new(Acl::new("t"))];
            let token = String::from("f11tt");
            println!("Processing {:?}", token);
            let mut machine = GlobMatcher::new();

            let mut acl_iterator = acl.iter();
            machine.step_in(&acl_iterator.next().unwrap().seq);
            for ch in token.chars() {
                if machine.is_sink() {
                    break;
                }
                machine.feed(Event::CharIn(ch));
                if !machine.accepts_more() {
                    match acl_iterator.next() {
                        None => break,
                        Some(acl) => machine.step_in(&acl.seq),
                    }
                }
            }
            println!("Machine is at {:?} state", machine.state());


            machine.feed(Event::EndOfStream);
            println!("Machine stopped in {:?} state", machine.state());
        }

        {
            let acl = TrieKey::new(Acl::new("f1*t"));
            let token = String::from("f01t");
            println!("Processing {:?}", token);
            let mut machine = GlobMatcher::new();
            machine.step_in(&acl.seq);
            for ch in token.chars() {
                machine.feed(Event::CharIn(ch));
                if machine.is_sink() {
                    break;
                }
            }
            println!("Machine stopped in {:?} state", machine.state())
        }

        {
            let acl = TrieKey::new(Acl::new("f1*"));
            let token = String::from("f01000000000");
            println!("Processing {:?}", token);
            let mut machine = GlobMatcher::new();
            machine.step_in(&acl.seq);
            for ch in token.chars() {
                machine.feed(Event::CharIn(ch));
                if machine.is_sink() {
                    break;
                }
            }
            println!("Machine stopped in {:?} state", machine.state())
        }

        {
            let acl = TrieKey::new(Acl::new("f1*"));
            let token = String::from("f10000000000");
            println!("Processing {:?}", token);
            let mut machine = GlobMatcher::new();
            machine.step_in(&acl.seq);
            for ch in token.chars() {
                machine.feed(Event::CharIn(ch));
                if machine.is_sink() {
                    break;
                }
            }
            println!("Machine stopped in {:?} state", machine.state())
        }

        {
            let acl = TrieKey::new(Acl::new("f1*"));
            let token = String::from("f20000000000");
            println!("Processing {:?}", token);
            let mut machine = GlobMatcher::new();
            machine.step_in(&acl.seq);
            for ch in token.chars() {
                machine.feed(Event::CharIn(ch));
                if machine.is_sink() {
                    break;
                }
            }
            println!("Machine stopped in {:?} state", machine.state())
        }

        {
            let acl = TrieKey::new(Acl::new("f20*1*F"));
            let token = String::from("f20000000001XXXF");
            println!("Processing {:?}", token);
            let mut machine = GlobMatcher::new();
            machine.step_in(&acl.seq);
            for ch in token.chars() {
                machine.feed(Event::CharIn(ch));
                if machine.is_sink() {
                    break;
                }
            }
            println!("Machine stopped in {:?} state", machine.state())
        }
    }
}