Skip to main content

actix_cloud/
utils.rs

1use std::{
2    collections::HashSet,
3    env,
4    hash::Hash,
5    process::{Child, Command},
6};
7
8use rand::{
9    distr::{Alphanumeric, Uniform},
10    rng, RngExt as _,
11};
12
13use crate::Result;
14
15/// Check whether iterator `iter` contains only unique values.
16pub fn is_unique<T>(iter: T) -> bool
17where
18    T: IntoIterator,
19    T::Item: Eq + Hash,
20{
21    let mut uniq = HashSet::new();
22    iter.into_iter().all(move |x| uniq.insert(x))
23}
24
25/// Check whether `t` type `T` is equal to default.
26pub fn is_default<T: Default + PartialEq>(t: &T) -> bool {
27    *t == Default::default()
28}
29
30/// Get `n` bytes random string.
31/// `[a-zA-Z0-9]+`
32pub fn rand_string(n: usize) -> String {
33    rng()
34        .sample_iter(&Alphanumeric)
35        .take(n)
36        .map(char::from)
37        .collect()
38}
39
40/// Get `n` bytes random hex string.
41/// `[a-f0-9]+`
42pub fn rand_string_hex(n: usize) -> String {
43    let mut rng = rng();
44    let bytes: Vec<u8> = (0..n.div_ceil(2)).map(|_| rng.random()).collect();
45    let mut s = hex::encode(bytes);
46    s.truncate(n);
47    s
48}
49
50/// Get `n` bytes random string (all printable ascii).
51pub fn rand_string_all(n: usize) -> String {
52    rng()
53        .sample_iter(Uniform::new(char::from(33), char::from(127)).unwrap())
54        .take(n)
55        .collect()
56}
57
58/// Restart the program and keep the argument.
59///
60/// Inherit the environment/io/working directory of current process.
61pub fn restart() -> Result<Child> {
62    Command::new(env::current_exe()?)
63        .args(env::args().skip(1))
64        .spawn()
65        .map_err(Into::into)
66}
67
68#[cfg(feature = "rustls")]
69/// Load SSL certificate and key.
70///
71/// Only PKCS 8 private keys are supported (`BEGIN PRIVATE KEY`). The aws-lc-rs
72/// provider is installed as the default rustls crypto provider.
73pub fn load_rustls_config<P: AsRef<std::path::Path>>(
74    cert: P,
75    key: P,
76) -> Result<rustls::ServerConfig> {
77    let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
78    let config = rustls::ServerConfig::builder().with_no_client_auth();
79    let cert_chain =
80        rustls_pemfile::certs(&mut std::io::BufReader::new(std::fs::File::open(cert)?))
81            .collect::<Result<Vec<_>, _>>()?;
82    let mut key_chain =
83        rustls_pemfile::pkcs8_private_keys(&mut std::io::BufReader::new(std::fs::File::open(key)?))
84            .map(|v| v.map(rustls::pki_types::PrivateKeyDer::Pkcs8))
85            .collect::<Result<Vec<_>, _>>()?;
86
87    let Some(private_key) = key_chain.pop() else {
88        anyhow::bail!("Cannot find PKCS 8 private key");
89    };
90
91    config
92        .with_single_cert(cert_chain, private_key)
93        .map_err(Into::into)
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn test_is_unique() {
102        assert!(is_unique(vec![1, 2, 3]));
103        assert!(is_unique([1, 2, 3]));
104        assert!(is_unique(vec![1]));
105        assert!(is_unique(Vec::<i32>::new()));
106        assert!(!is_unique(vec![1, 2, 2]));
107        assert!(!is_unique([1, 1]));
108        assert!(is_unique("abc".chars()));
109        assert!(!is_unique("aba".chars()));
110        assert!(is_unique("".chars()));
111    }
112
113    #[test]
114    fn test_is_default() {
115        assert!(is_default(&0_u8));
116        assert!(is_default(&0_i64));
117        assert!(!is_default(&1_i64));
118        assert!(is_default(&String::new()));
119        assert!(!is_default(&"a".to_string()));
120        assert!(is_default(&Vec::<i32>::new()));
121        assert!(!is_default(&vec![1]));
122        assert!(is_default(&Option::<i32>::None));
123        assert!(!is_default(&Some(1)));
124    }
125
126    #[test]
127    fn test_rand_string() {
128        for n in [0, 1, 2, 3, 4, 5, 10, 63, 64] {
129            let s = rand_string(n);
130            assert_eq!(s.len(), n);
131            assert!(s.chars().all(|c| c.is_ascii_alphanumeric()));
132        }
133    }
134
135    #[test]
136    fn test_rand_string_hex() {
137        for n in [0, 1, 2, 3, 4, 5, 10, 63, 64] {
138            let s = rand_string_hex(n);
139            assert_eq!(s.len(), n);
140            assert!(s.bytes().all(|b| b.is_ascii_hexdigit()));
141        }
142    }
143
144    #[test]
145    fn test_rand_string_all() {
146        let s = rand_string_all(4000);
147        assert!(s.chars().all(|c| ('!'..='~').contains(&c)),);
148    }
149}