Skip to main content

otter_support/
support.rs

1// Copyright 2020-2021 Ian Jackson and contributors to Otter
2// SPDX-License-Identifier: AGPL-3.0-or-later
3// There is NO WARRANTY.
4
5use crate::prelude::*;
6
7//========== miscellany ==========
8// (roughly in order of implementation length)
9
10//---------- IpAddress ----------
11
12pub trait IpAddress: Debug {
13  fn with_port(&self, port: u16) -> SocketAddr;
14}
15
16impl<A> IpAddress for A where A: Into<IpAddr> + Debug + Clone {
17  fn with_port(&self, port: u16) -> SocketAddr {
18    match (self.clone().into(), port)
19      .to_socket_addrs()
20      .map(|i| i.at_most_one()) {
21        Ok(Ok(Some(addr))) => addr,
22        x => panic!("{:?},{} gave {:?}", self, port, x),
23      }
24  }
25}
26
27//========== toml ====================
28
29#[derive(Debug,Copy,Clone,Eq,PartialEq,Ord,PartialOrd)]
30pub struct TomlQuote<'s>(pub &'s str);
31
32// We reimplement this because the toml crate doesn't expose it, and
33// looking at the github issues etc. for that crate isn't encuraging.
34impl<'s> Display for TomlQuote<'s> {
35  #[throws(fmt::Error)]
36  fn fmt(&self, f: &mut fmt::Formatter) {
37    for c in self.0.chars() {
38      match c {
39        '"' | '\\'=> write!(f, "\\{}", c)?,
40        c if (c < ' ' && c != '\t') || c == '\x7f' => {
41          write!(f, r#"\u{:04x}"#, c as u32).unwrap();
42          continue;
43        }
44        c => write!(f, "{}", c)?,
45      }
46    }
47  }
48}
49
50#[test]
51fn toml_quote_string_test(){
52  assert_eq!(TomlQuote(r#"w \ "	ƒ."#).to_string(),
53                       r#"w \\ \"	\u0007\u007fƒ."#);
54}
55
56pub fn toml_merge<'u,
57                  S: 'u + AsRef<str>,
58                  KV: IntoIterator<Item=(&'u S, &'u toml::Value)>
59                  >(
60  table: &mut toml::value::Table,
61  updates: KV,
62) {
63  use toml::value::{Table, Value};
64  type TME<'e> = toml::map::Entry<'e>;
65
66  let mut kv = updates.into_iter().map(|(k, v)| (k.as_ref(), v));
67  inner(table, &mut kv);
68
69  fn inner<'u>(
70    table: &mut Table,
71    updates: &'u mut dyn Iterator<Item=(&'u str, &'u Value)>
72  ) {
73    for (k, v) in updates {
74      let e = table.entry(k);
75      match e {
76        TME::Vacant(ve) => {
77          ve.insert(v.clone());
78        }
79        TME::Occupied(mut oe) => match (oe.get_mut(), v) {
80          (Value::Table(old), Value::Table(new)) => {
81            toml_merge(old, new);
82          }
83          (Value::Array(old), Value::Array(new)) => {
84            old.extend(new.iter().cloned());
85          }
86          (old, new) => {
87            *old = new.clone();
88          }
89        }
90      }
91    }
92  }
93}
94
95//========== Timestamp ==========
96
97#[derive(Copy,Clone,Debug,Serialize,Deserialize,Eq,Ord,PartialEq,PartialOrd)]
98#[serde(transparent)]
99pub struct Timestamp(pub u64); /* time_t */
100
101impl Timestamp {
102  /// Always >= previously
103  pub fn now() -> Timestamp {
104    use std::time::SystemTime;
105    let now = SystemTime::now()
106      .duration_since(SystemTime::UNIX_EPOCH)
107      .unwrap()
108      .as_secs();
109    Timestamp(now)
110  }
111
112  pub fn render(&self, tz: &Timezone) -> String {
113    tz.format(*self)
114  }
115}
116
117//========== miscellaneous macros ==========
118
119paste!{
120  #[cfg(debug_assertions)]
121  pub fn [<x x x>]<T>() -> T { panic!("todo item triggered") }
122}
123
124#[macro_export]
125macro_rules! trace_dbg {
126  ($msg:expr $(,$val:expr)*) => {
127    if log_enabled!(log::Level::Trace) {
128      #[allow(unused_mut)]
129      let mut buf = format!("{}", &$msg);
130      $( write!(&mut buf, " {}={:?}", stringify!($val), &$val).unwrap(); )*
131      trace!("{}", buf);
132    }
133  }
134
135}
136
137//========== matches_doesnot ==========
138
139#[macro_export] // <- otherwise bogus warning `unused_macros`
140macro_rules! matches_doesnot_yn2bool {
141  (=) => (true);
142  (!) => (false);
143}
144
145#[macro_export]
146macro_rules! matches_doesnot {
147  ($v:expr,
148   $(
149     $yn:tt $p:pat
150   ),* $(,)?
151  ) => {
152    match $v {
153      $(
154        $p => $crate::matches_doesnot_yn2bool!($yn),
155      )*
156    }
157  }
158}
159
160#[test]
161fn matches_doesnot_test() {
162  assert!(
163    matches_doesnot!(
164      Some(42),
165      = Some(_),
166      ! None
167    )
168  );
169  assert!(
170    matches_doesnot!(
171      Some(42),
172      ! None,
173      ! Some(3),
174      = Some(_),
175    )
176  );
177  assert!(
178    matches_doesnot!(
179      Some(1),
180      = Some(1) | Some(2),
181      ! Some(_) | None
182    )
183  );
184  assert!(
185    ! matches_doesnot!(
186      Some(1),
187      ! Some(1) | Some(2),
188      = Some(_) | None
189    )
190  );
191}
192