submap 0.3.6

B-Tree map for pub/sub services
Documentation
use std::str::Split;

#[allow(clippy::wildcard_imports)]
use crate::types::*;

#[derive(Debug, Clone)]
struct Broadcast<C> {
    childs: Map<String, Broadcast<C>>,
    childs_any: Option<Box<Broadcast<C>>>,
    members: Set<C>,
    members_wildcard: Set<C>,
}

impl<C> Broadcast<C> {
    #[inline]
    fn is_empty(&self) -> bool {
        self.childs.is_empty() && self.members.is_empty()
    }
}

impl<C> Default for Broadcast<C> {
    fn default() -> Self {
        Self {
            childs: <_>::default(),
            childs_any: <_>::default(),
            members: <_>::default(),
            members_wildcard: <_>::default(),
        }
    }
}

#[derive(Debug, Clone)]
pub struct BroadcastMap<C> {
    broadcasts: Broadcast<C>,
    separator: char,
    match_any: Set<String>,
    wildcard: Set<String>,
}

impl<C> Default for BroadcastMap<C> {
    fn default() -> Self {
        Self {
            broadcasts: Broadcast::default(),
            separator: '.',
            match_any: vec!["?".to_owned()].into_iter().collect(),
            wildcard: vec!["*".to_owned()].into_iter().collect(),
        }
    }
}

impl<C> BroadcastMap<C>
where
    C: Client,
{
    #[inline]
    pub fn new() -> Self {
        Self::default()
    }
    #[inline]
    pub fn separator(mut self, separator: char) -> Self {
        self.separator = separator;
        self
    }
    #[inline]
    pub fn wildcard(mut self, wildcard: &str) -> Self {
        self.wildcard = vec![wildcard.to_owned()].into_iter().collect();
        self
    }
    #[inline]
    pub fn match_any(mut self, match_any: &str) -> Self {
        self.match_any = vec![match_any.to_owned()].into_iter().collect();
        self
    }
    #[inline]
    pub fn wildcard_multiple(mut self, wildcard_multiple: &[&str]) -> Self {
        self.wildcard = wildcard_multiple.iter().map(|&v| v.to_owned()).collect();
        self
    }
    #[inline]
    pub fn match_any_multiple(mut self, match_any_multiple: &[&str]) -> Self {
        self.match_any = match_any_multiple.iter().map(|&v| v.to_owned()).collect();
        self
    }
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.broadcasts.is_empty()
    }
    #[inline]
    pub fn register_client(&mut self, name: &str, client: &C) {
        register_broadcast_client_rec(&mut self.broadcasts, name.split(self.separator), client);
    }
    #[inline]
    pub fn unregister_client(&mut self, name: &str, client: &C) {
        unregister_broadcast_client_rec(&mut self.broadcasts, name.split(self.separator), client);
    }
    pub fn get_clients_by_mask(&self, mask: &str) -> Set<C> {
        let mut result = Set::new();
        get_broadcast_clients_rec(
            &self.broadcasts,
            mask.split(self.separator),
            &mut result,
            &self.wildcard,
            &self.match_any,
        );
        result
    }
}

fn get_broadcast_clients_rec<C>(
    broadcast: &Broadcast<C>,
    mut sp: Split<char>,
    result: &mut Set<C>,
    wildcard: &Set<String>,
    match_any: &Set<String>,
) where
    C: Client,
{
    if let Some(chunk) = sp.next() {
        if wildcard.contains(chunk) {
            result.extend(broadcast.members_wildcard.clone());
        } else if match_any.contains(chunk) {
            if let Some(ref child) = broadcast.childs_any {
                get_broadcast_clients_rec(child, sp, result, wildcard, match_any);
            }
        } else if let Some(child) = broadcast.childs.get(chunk) {
            get_broadcast_clients_rec(child, sp, result, wildcard, match_any);
        }
    } else {
        result.extend(broadcast.members.clone());
    }
}

fn register_broadcast_client_rec<C>(broadcast: &mut Broadcast<C>, mut sp: Split<char>, client: &C)
where
    C: Client,
{
    if let Some(chunk) = sp.next() {
        broadcast.members_wildcard.insert(client.clone());
        if let Some(c) = broadcast.childs.get_mut(chunk) {
            register_broadcast_client_rec(c, sp.clone(), client);
        } else {
            let mut child = Broadcast::default();
            register_broadcast_client_rec(&mut child, sp.clone(), client);
            broadcast.childs.insert(chunk.to_owned(), child);
        }
        if let Some(ref mut c) = broadcast.childs_any {
            register_broadcast_client_rec(c, sp, client);
        } else {
            let mut child = Broadcast::default();
            register_broadcast_client_rec(&mut child, sp, client);
            broadcast.childs_any.replace(Box::new(child));
        }
    } else {
        broadcast.members.insert(client.clone());
    }
}

fn unregister_broadcast_client_rec<C>(broadcast: &mut Broadcast<C>, mut sp: Split<char>, client: &C)
where
    C: Client,
{
    if let Some(chunk) = sp.next() {
        broadcast.members_wildcard.remove(client);
        if let Some(c) = broadcast.childs.get_mut(chunk) {
            unregister_broadcast_client_rec(c, sp.clone(), client);
            if c.is_empty() {
                broadcast.childs.remove(chunk);
            }
        }
        if let Some(ref mut c) = broadcast.childs_any {
            unregister_broadcast_client_rec(c, sp, client);
            if c.is_empty() {
                broadcast.childs_any = None;
            }
        }
    } else {
        broadcast.members.remove(client);
    }
}

#[cfg(test)]
mod test {
    use super::BroadcastMap;
    #[test]
    #[allow(clippy::similar_names)]
    fn test_broadcast() {
        let mut bmap: BroadcastMap<u32> = BroadcastMap::new().separator('/');
        let client1: u32 = 1;
        let client2: u32 = 2;
        let client3: u32 = 3;
        let client4: u32 = 4;
        let client5: u32 = 5;
        bmap.register_client("this/is/a", &client1);
        bmap.register_client("this/is/b", &client2);
        bmap.register_client("this/is", &client3);
        bmap.register_client("this", &client4);
        bmap.register_client("that/is/a", &client5);
        let clients = bmap.get_clients_by_mask("this/is/*");
        assert!(clients.contains(&client1));
        assert!(clients.contains(&client2));
        assert!(!clients.contains(&client3));
        assert!(!clients.contains(&client4));
        assert!(!clients.contains(&client5));
        let clients = bmap.get_clients_by_mask("this/is");
        assert!(!clients.contains(&client1));
        assert!(!clients.contains(&client2));
        assert!(clients.contains(&client3));
        assert!(!clients.contains(&client4));
        assert!(!clients.contains(&client5));
        let clients = bmap.get_clients_by_mask("this");
        assert!(!clients.contains(&client1));
        assert!(!clients.contains(&client2));
        assert!(!clients.contains(&client3));
        assert!(clients.contains(&client4));
        assert!(!clients.contains(&client5));
        let clients = bmap.get_clients_by_mask("this/*");
        assert!(clients.contains(&client1));
        assert!(clients.contains(&client2));
        assert!(clients.contains(&client3));
        assert!(!clients.contains(&client4));
        assert!(!clients.contains(&client5));
        let clients = bmap.get_clients_by_mask("this/is/*");
        assert!(clients.contains(&client1));
        assert!(clients.contains(&client2));
        assert!(!clients.contains(&client3));
        assert!(!clients.contains(&client4));
        assert!(!clients.contains(&client5));
        let clients = bmap.get_clients_by_mask("*");
        assert!(clients.contains(&client1));
        assert!(clients.contains(&client2));
        assert!(clients.contains(&client3));
        assert!(clients.contains(&client4));
        assert!(clients.contains(&client5));
        let clients = bmap.get_clients_by_mask("this/is/a/*");
        assert!(!clients.contains(&client1));
        assert!(!clients.contains(&client2));
        assert!(!clients.contains(&client3));
        assert!(!clients.contains(&client4));
        assert!(!clients.contains(&client5));
        let clients = bmap.get_clients_by_mask("this/is/a/?");
        assert!(!clients.contains(&client1));
        assert!(!clients.contains(&client2));
        assert!(!clients.contains(&client3));
        assert!(!clients.contains(&client4));
        assert!(!clients.contains(&client5));
        let clients = bmap.get_clients_by_mask("this/?/a");
        assert!(clients.contains(&client1));
        assert!(!clients.contains(&client2));
        assert!(!clients.contains(&client3));
        assert!(!clients.contains(&client4));
        assert!(!clients.contains(&client5));
        let clients = bmap.get_clients_by_mask("this/?/?");
        assert!(clients.contains(&client1));
        assert!(clients.contains(&client2));
        assert!(!clients.contains(&client3));
        assert!(!clients.contains(&client4));
        assert!(!clients.contains(&client5));
        let clients = bmap.get_clients_by_mask("?/is/a");
        assert!(clients.contains(&client1));
        assert!(!clients.contains(&client2));
        assert!(!clients.contains(&client3));
        assert!(!clients.contains(&client4));
        assert!(clients.contains(&client5));
        bmap.unregister_client("this/is/a", &client1);
        bmap.unregister_client("this/is/b", &client2);
        bmap.unregister_client("this/is", &client3);
        bmap.unregister_client("this", &client4);
        bmap.unregister_client("that/is/a", &client5);
        assert!(bmap.broadcasts.is_empty());
    }
}