modio-mqttbridge 0.6.1

Modio on-device MQTT bridge
// Author: D.S. Ljungmark <spider@skuggor.se>, Modio AB
// SPDX-License-Identifier: AGPL-3.0-or-later
use std::fmt;
// Needed so we have "is_empty()" on the trie
use radix_trie::TrieCommon;

// Since we dont store a value, we pass in the () empty set
type MatchesTrie = radix_trie::Trie<String, ()>;

/// Our matches struct, very simple inner data
pub struct Matches {
    matches: MatchesTrie,
}

impl Matches {
    /// consume a vector of key prefixes into matches by turning it to a trie
    pub fn new(mut key_prefixes: Vec<String>) -> Matches {
        const EMPTY: () = ();
        let matches: MatchesTrie = key_prefixes.drain(..).map(|x| (x, EMPTY)).collect();
        Matches { matches }
    }

    /// Returns true if the key is found, or if the matches list is empty.
    pub fn key_matches(&self, key: &str) -> bool {
        if self.matches.is_empty() {
            true
        } else {
            self.matches.get_ancestor(key).is_some()
        }
    }
}

impl fmt::Debug for Matches {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_set().entries(self.matches.keys()).finish()
    }
}

#[cfg(test)]
mod test {
    use super::Matches;
    use std::result::Result;
    type TestResult = Result<(), Box<dyn std::error::Error>>;

    #[test]
    fn matches_assumption_works() -> TestResult {
        let prefix = vec![
            "abc.def".to_string(),
            "abc.def.ghi".to_string(),
            "cde.def.ghi".to_string(),
        ];
        let matches = Matches::new(prefix);
        assert!(matches.key_matches("abc.def"));
        assert!(matches.key_matches("abc.def.ghi"));
        assert!(matches.key_matches("abc.def.ghi.bar.bar.bar"));
        assert!(!matches.key_matches("abcdef"));
        assert!(!matches.key_matches(""));
        assert!(!matches.key_matches("cde"));
        assert!(!matches.key_matches("abc.del"));
        assert!(!matches.key_matches("cde.def.gh"));
        Ok(())
    }

    #[test]
    fn empty_matches_work() -> TestResult {
        let prefix = Vec::<String>::new();
        let matches = Matches::new(prefix);
        assert!(matches.key_matches("abc.def"));
        assert!(matches.key_matches("cde"));
        assert!(matches.key_matches("abc.del"));
        assert!(matches.key_matches("cde.def.gh"));
        Ok(())
    }
}