libtelnet_rs/
compatibility.rs

1/// An expansion of a bitmask contained in `CompatibilityTable`.
2#[derive(Clone, Copy)]
3pub struct CompatibilityEntry {
4  /// Whether we support this option from us -> them.
5  pub local: bool,
6  /// Whether we support this option from them -> us.
7  pub remote: bool,
8  /// Whether this option is locally enabled.
9  pub local_state: bool,
10  /// Whether this option is remotely enabled.
11  pub remote_state: bool,
12}
13
14impl CompatibilityEntry {
15  pub fn new(local: bool, remote: bool, local_state: bool, remote_state: bool) -> Self {
16    Self {
17      local,
18      remote,
19      local_state,
20      remote_state,
21    }
22  }
23  /// Creates a u8 bitmask from this entry.
24  pub fn into_u8(self) -> u8 {
25    let mut res: u8 = 0;
26    if self.local {
27      res |= CompatibilityTable::ENABLED_LOCAL;
28    }
29    if self.remote {
30      res |= CompatibilityTable::ENABLED_REMOTE;
31    }
32    if self.local_state {
33      res |= CompatibilityTable::LOCAL_STATE;
34    }
35    if self.remote_state {
36      res |= CompatibilityTable::REMOTE_STATE;
37    }
38    res
39  }
40  /// Expands a u8 bitmask into a CompatibilityEntry.
41  pub fn from(value: u8) -> Self {
42    Self {
43      local: value & CompatibilityTable::ENABLED_LOCAL == CompatibilityTable::ENABLED_LOCAL,
44      remote: value & CompatibilityTable::ENABLED_REMOTE == CompatibilityTable::ENABLED_REMOTE,
45      local_state: value & CompatibilityTable::LOCAL_STATE == CompatibilityTable::LOCAL_STATE,
46      remote_state: value & CompatibilityTable::REMOTE_STATE == CompatibilityTable::REMOTE_STATE,
47    }
48  }
49}
50
51/// A table of options that are supported locally or remotely, and their current state.
52#[derive(Clone)]
53pub struct CompatibilityTable {
54  options: [u8; 256],
55}
56
57impl Default for CompatibilityTable {
58  fn default() -> Self {
59    Self { options: [0; 256] }
60  }
61}
62
63impl CompatibilityTable {
64  /// Option is locally supported.
65  pub const ENABLED_LOCAL: u8 = 1;
66  /// Option is remotely supported.
67  pub const ENABLED_REMOTE: u8 = 1 << 1;
68  /// Option is currently enabled locally.
69  pub const LOCAL_STATE: u8 = 1 << 2;
70  /// Option is currently enabled remotely.
71  pub const REMOTE_STATE: u8 = 1 << 3;
72  pub fn new() -> Self {
73    Self::default()
74  }
75  /// Create a table with some option values set.
76  ///
77  /// # Arguments
78  ///
79  /// `values` - A slice of `(u8, u8)` tuples. The first value is the option code, and the second is the bitmask value for that option.
80  ///
81  /// # Notes
82  ///
83  /// An option bitmask can be generated using the `CompatibilityEntry` struct, using `entry.into_u8()`.
84  pub fn from_options(values: &[(u8, u8)]) -> Self {
85    let mut options: [u8; 256] = [0; 256];
86    for (opt, val) in values {
87      options[*opt as usize] = *val;
88    }
89    Self { options }
90  }
91  /// Enable local support for an option.
92  pub fn support_local(&mut self, option: u8) {
93    let mut opt = CompatibilityEntry::from(self.options[option as usize]);
94    opt.local = true;
95    self.set_option(option, opt);
96  }
97  /// Enable remote support for an option.
98  pub fn support_remote(&mut self, option: u8) {
99    let mut opt = CompatibilityEntry::from(self.options[option as usize]);
100    opt.remote = true;
101    self.set_option(option, opt);
102  }
103  /// Enable both remote and local support for an option.
104  pub fn support(&mut self, option: u8) {
105    let mut opt = CompatibilityEntry::from(self.options[option as usize]);
106    opt.local = true;
107    opt.remote = true;
108    self.set_option(option, opt);
109  }
110  /// Retrieve a `CompatbilityEntry` generated from the current state of the option value.
111  pub fn get_option(&self, option: u8) -> CompatibilityEntry {
112    CompatibilityEntry::from(self.options[option as usize])
113  }
114  /// Set an option value by getting the bitmask from a `CompatibilityEntry`.
115  pub fn set_option(&mut self, option: u8, entry: CompatibilityEntry) {
116    self.options[option as usize] = entry.clone().into_u8();
117  }
118
119  /// Reset all negotiated states
120  pub fn reset_states(&mut self) {
121    for opt in self.options.iter_mut() {
122      let mut entry = CompatibilityEntry::from(*opt);
123      entry.local_state = false;
124      entry.remote_state = false;
125      *opt = entry.into_u8();
126    }
127  }
128}
129
130#[cfg(test)]
131mod test_compat {
132  use super::*;
133
134  #[test]
135  fn test_reset() {
136    let mut table = CompatibilityTable::default();
137    let entry = CompatibilityEntry::new(true, true, true, true);
138    assert_eq!(entry.remote, true);
139    assert_eq!(entry.local, true);
140    assert_eq!(entry.remote_state, true);
141    assert_eq!(entry.local_state, true);
142    table.set_option(201, entry);
143    table.reset_states();
144    let entry = table.get_option(201);
145    assert_eq!(entry.remote, true);
146    assert_eq!(entry.local, true);
147    assert_eq!(entry.remote_state, false);
148    assert_eq!(entry.local_state, false);
149  }
150}