ssh2_config/
host.rs

1//! # host
2//!
3//! Ssh host type
4
5use wildmatch::WildMatch;
6
7use super::HostParams;
8
9/// Describes the rules to be used for a certain host
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct Host {
12    /// List of hosts for which params are valid. String is string pattern, bool is whether condition is negated
13    pub pattern: Vec<HostClause>,
14    pub params: HostParams,
15}
16
17impl Host {
18    pub fn new(pattern: Vec<HostClause>, params: HostParams) -> Self {
19        Self { pattern, params }
20    }
21
22    /// Returns whether `host` argument intersects the host clauses
23    pub fn intersects(&self, host: &str) -> bool {
24        let mut has_matched = false;
25        for entry in self.pattern.iter() {
26            let matches = entry.intersects(host);
27            // If the entry is negated and it matches we can stop searching
28            if matches && entry.negated {
29                return false;
30            }
31            has_matched |= matches;
32        }
33        has_matched
34    }
35}
36
37/// Describes a single clause to match host
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct HostClause {
40    pub pattern: String,
41    pub negated: bool,
42}
43
44impl HostClause {
45    /// Creates a new `HostClause` from arguments
46    pub fn new(pattern: String, negated: bool) -> Self {
47        Self { pattern, negated }
48    }
49
50    /// Returns whether `host` argument intersects the clause
51    pub fn intersects(&self, host: &str) -> bool {
52        WildMatch::new(self.pattern.as_str()).matches(host)
53    }
54}
55
56#[cfg(test)]
57mod test {
58
59    use pretty_assertions::assert_eq;
60
61    use super::*;
62
63    #[test]
64    fn should_build_host_clause() {
65        let clause = HostClause::new("192.168.1.1".to_string(), false);
66        assert_eq!(clause.pattern.as_str(), "192.168.1.1");
67        assert_eq!(clause.negated, false);
68    }
69
70    #[test]
71    fn should_intersect_host_clause() {
72        let clause = HostClause::new("192.168.*.*".to_string(), false);
73        assert!(clause.intersects("192.168.2.30"));
74        let clause = HostClause::new("192.168.?0.*".to_string(), false);
75        assert!(clause.intersects("192.168.40.28"));
76    }
77
78    #[test]
79    fn should_not_intersect_host_clause() {
80        let clause = HostClause::new("192.168.*.*".to_string(), false);
81        assert_eq!(clause.intersects("172.26.104.4"), false);
82    }
83
84    #[test]
85    fn should_init_host() {
86        let host = Host::new(
87            vec![HostClause::new("192.168.*.*".to_string(), false)],
88            HostParams::default(),
89        );
90        assert_eq!(host.pattern.len(), 1);
91    }
92
93    #[test]
94    fn should_intersect_clause() {
95        let host = Host::new(
96            vec![
97                HostClause::new("192.168.*.*".to_string(), false),
98                HostClause::new("172.26.*.*".to_string(), false),
99                HostClause::new("10.8.*.*".to_string(), false),
100                HostClause::new("10.8.0.8".to_string(), true),
101            ],
102            HostParams::default(),
103        );
104        assert!(host.intersects("192.168.1.32"));
105        assert!(host.intersects("172.26.104.4"));
106        assert!(host.intersects("10.8.0.10"));
107    }
108
109    #[test]
110    fn should_not_intersect_clause() {
111        let host = Host::new(
112            vec![
113                HostClause::new("192.168.*.*".to_string(), false),
114                HostClause::new("172.26.*.*".to_string(), false),
115                HostClause::new("10.8.*.*".to_string(), false),
116                HostClause::new("10.8.0.8".to_string(), true),
117            ],
118            HostParams::default(),
119        );
120        assert_eq!(host.intersects("192.169.1.32"), false);
121        assert_eq!(host.intersects("172.28.104.4"), false);
122        assert_eq!(host.intersects("10.9.0.8"), false);
123        assert_eq!(host.intersects("10.8.0.8"), false);
124    }
125}