2024-07-20 05:12:49 +02:00
|
|
|
//! `utils.rs` - the projects kitchen junk drawer.
|
|
|
|
|
2023-10-08 09:39:00 +02:00
|
|
|
use crate::prelude::*;
|
2022-10-01 08:08:51 +02:00
|
|
|
use hashbrown::HashSet;
|
2024-04-27 15:22:39 +02:00
|
|
|
use rand::distributions::{Distribution, Uniform};
|
2019-10-07 00:41:30 +02:00
|
|
|
use rand::{thread_rng, Rng};
|
2024-08-29 03:38:00 +02:00
|
|
|
use std::collections::BTreeSet;
|
2024-07-20 05:12:49 +02:00
|
|
|
use std::ops::Range;
|
2021-06-27 03:30:40 +02:00
|
|
|
|
133 limit to human readable characters. (#174)
Implements #133, limit password generators to distict human readable characters. This removes the common confusions such as I,l, 1, 0, O, o, m,rn, etc . This in mind, they may not all have been found, but it should be easier now to improve upon.
2020-01-27 03:56:15 +01:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct DistinctAlpha;
|
|
|
|
|
2021-05-21 08:35:09 +02:00
|
|
|
pub type Sid = [u8; 4];
|
2019-09-06 05:04:58 +02:00
|
|
|
|
2022-05-24 02:49:34 +02:00
|
|
|
pub fn uuid_to_gid_u32(u: Uuid) -> u32 {
|
2019-11-30 00:39:31 +01:00
|
|
|
let b_ref = u.as_bytes();
|
|
|
|
let mut x: [u8; 4] = [0; 4];
|
|
|
|
x.clone_from_slice(&b_ref[12..16]);
|
|
|
|
u32::from_be_bytes(x)
|
|
|
|
}
|
|
|
|
|
2021-05-21 08:35:09 +02:00
|
|
|
fn uuid_from_u64_u32(a: u64, b: u32, sid: Sid) -> Uuid {
|
2019-09-06 05:04:58 +02:00
|
|
|
let mut v: Vec<u8> = Vec::with_capacity(16);
|
|
|
|
v.extend_from_slice(&a.to_be_bytes());
|
|
|
|
v.extend_from_slice(&b.to_be_bytes());
|
2020-01-09 11:07:14 +01:00
|
|
|
v.extend_from_slice(&sid);
|
2019-09-06 05:04:58 +02:00
|
|
|
|
2020-08-04 04:58:11 +02:00
|
|
|
#[allow(clippy::expect_used)]
|
2022-12-18 04:26:20 +01:00
|
|
|
uuid::Builder::from_slice(v.as_slice())
|
2020-08-04 04:58:11 +02:00
|
|
|
.expect("invalid slice for uuid builder")
|
2022-04-27 05:35:26 +02:00
|
|
|
.into_uuid()
|
2019-09-06 05:04:58 +02:00
|
|
|
}
|
|
|
|
|
2021-05-21 08:35:09 +02:00
|
|
|
pub fn uuid_from_duration(d: Duration, sid: Sid) -> Uuid {
|
2019-09-06 05:04:58 +02:00
|
|
|
uuid_from_u64_u32(d.as_secs(), d.subsec_nanos(), sid)
|
|
|
|
}
|
|
|
|
|
2023-11-19 12:56:19 +01:00
|
|
|
pub(crate) fn password_from_random_len(len: u32) -> String {
|
|
|
|
thread_rng()
|
|
|
|
.sample_iter(&DistinctAlpha)
|
|
|
|
.take(len as usize)
|
|
|
|
.collect::<String>()
|
|
|
|
}
|
|
|
|
|
2019-10-07 00:41:30 +02:00
|
|
|
pub fn password_from_random() -> String {
|
2023-11-19 12:56:19 +01:00
|
|
|
password_from_random_len(48)
|
2019-10-07 00:41:30 +02:00
|
|
|
}
|
|
|
|
|
2021-06-25 04:39:05 +02:00
|
|
|
pub fn backup_code_from_random() -> HashSet<String> {
|
2023-04-26 13:55:42 +02:00
|
|
|
(0..8).map(|_| readable_password_from_random()).collect()
|
2021-06-25 04:39:05 +02:00
|
|
|
}
|
|
|
|
|
2019-10-31 01:48:15 +01:00
|
|
|
pub fn readable_password_from_random() -> String {
|
2022-05-24 02:49:34 +02:00
|
|
|
// 2^112 bits, means we need at least 55^20 to have as many bits of entropy.
|
|
|
|
// this leads us to 4 groups of 5 to create 55^20
|
2021-01-10 04:41:56 +01:00
|
|
|
let mut trng = thread_rng();
|
2019-10-31 01:48:15 +01:00
|
|
|
format!(
|
|
|
|
"{}-{}-{}-{}",
|
2021-01-10 04:41:56 +01:00
|
|
|
(&mut trng)
|
|
|
|
.sample_iter(&DistinctAlpha)
|
2022-05-24 02:49:34 +02:00
|
|
|
.take(5)
|
2021-01-10 04:41:56 +01:00
|
|
|
.collect::<String>(),
|
|
|
|
(&mut trng)
|
|
|
|
.sample_iter(&DistinctAlpha)
|
2022-05-24 02:49:34 +02:00
|
|
|
.take(5)
|
2021-01-10 04:41:56 +01:00
|
|
|
.collect::<String>(),
|
|
|
|
(&mut trng)
|
|
|
|
.sample_iter(&DistinctAlpha)
|
2022-05-24 02:49:34 +02:00
|
|
|
.take(5)
|
2021-01-10 04:41:56 +01:00
|
|
|
.collect::<String>(),
|
|
|
|
(&mut trng)
|
|
|
|
.sample_iter(&DistinctAlpha)
|
2022-05-24 02:49:34 +02:00
|
|
|
.take(5)
|
2021-01-10 04:41:56 +01:00
|
|
|
.collect::<String>(),
|
2019-10-31 01:48:15 +01:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-08-29 03:38:00 +02:00
|
|
|
pub fn str_join(set: &BTreeSet<String>) -> String {
|
|
|
|
let alloc_len = set.iter().fold(0, |acc, s| acc + s.len() + 1);
|
|
|
|
let mut buf = String::with_capacity(alloc_len);
|
|
|
|
set.iter().for_each(|s| {
|
|
|
|
buf.push_str(s);
|
|
|
|
buf.push(' ');
|
|
|
|
});
|
|
|
|
|
|
|
|
// Remove the excess trailing space.
|
|
|
|
let _ = buf.pop();
|
|
|
|
|
|
|
|
buf
|
|
|
|
}
|
|
|
|
|
133 limit to human readable characters. (#174)
Implements #133, limit password generators to distict human readable characters. This removes the common confusions such as I,l, 1, 0, O, o, m,rn, etc . This in mind, they may not all have been found, but it should be easier now to improve upon.
2020-01-27 03:56:15 +01:00
|
|
|
impl Distribution<char> for DistinctAlpha {
|
|
|
|
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> char {
|
|
|
|
const RANGE: u32 = 55;
|
|
|
|
const GEN_ASCII_STR_CHARSET: &[u8] = b"ABCDEFGHJKLMNPQRSTUVWXYZ\
|
|
|
|
abcdefghjkpqrstuvwxyz\
|
|
|
|
0123456789";
|
2024-04-27 15:22:39 +02:00
|
|
|
|
|
|
|
let range = Uniform::new(0, RANGE);
|
|
|
|
|
|
|
|
let n = range.sample(rng);
|
|
|
|
GEN_ASCII_STR_CHARSET[n as usize] as char
|
133 limit to human readable characters. (#174)
Implements #133, limit password generators to distict human readable characters. This removes the common confusions such as I,l, 1, 0, O, o, m,rn, etc . This in mind, they may not all have been found, but it should be easier now to improve upon.
2020-01-27 03:56:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-20 05:12:49 +02:00
|
|
|
pub(crate) struct GraphemeClusterIter<'a> {
|
|
|
|
value: &'a str,
|
|
|
|
char_bounds: Vec<usize>,
|
|
|
|
window: usize,
|
|
|
|
range: Range<usize>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> GraphemeClusterIter<'a> {
|
|
|
|
pub fn new(value: &'a str, window: usize) -> Self {
|
|
|
|
let char_bounds = if value.len() < window {
|
|
|
|
Vec::with_capacity(0)
|
|
|
|
} else {
|
|
|
|
let mut char_bounds = Vec::with_capacity(value.len());
|
|
|
|
for idx in 0..value.len() {
|
|
|
|
if value.is_char_boundary(idx) {
|
|
|
|
char_bounds.push(idx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
char_bounds.push(value.len());
|
|
|
|
char_bounds
|
|
|
|
};
|
|
|
|
|
2024-07-26 09:02:37 +02:00
|
|
|
let window_max = char_bounds.len().saturating_sub(window);
|
2024-07-20 05:12:49 +02:00
|
|
|
let range = 0..window_max;
|
|
|
|
|
|
|
|
GraphemeClusterIter {
|
|
|
|
value,
|
|
|
|
char_bounds,
|
|
|
|
window,
|
|
|
|
range,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Iterator for GraphemeClusterIter<'a> {
|
|
|
|
type Item = &'a str;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<&'a str> {
|
|
|
|
self.range.next().map(|idx| {
|
|
|
|
let min = self.char_bounds[idx];
|
|
|
|
let max = self.char_bounds[idx + self.window];
|
|
|
|
&self.value[min..max]
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
2024-07-26 09:02:37 +02:00
|
|
|
let clusters = self.char_bounds.len().saturating_sub(1);
|
2024-07-20 05:12:49 +02:00
|
|
|
(clusters, Some(clusters))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn trigraph_iter(value: &str) -> impl Iterator<Item = &str> {
|
|
|
|
GraphemeClusterIter::new(value, 3)
|
|
|
|
.chain(GraphemeClusterIter::new(value, 2))
|
|
|
|
.chain(GraphemeClusterIter::new(value, 1))
|
|
|
|
}
|
|
|
|
|
2019-09-06 05:04:58 +02:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2022-12-18 04:26:20 +01:00
|
|
|
use crate::prelude::*;
|
2019-09-06 05:04:58 +02:00
|
|
|
use std::time::Duration;
|
2022-10-01 08:08:51 +02:00
|
|
|
|
2024-07-20 05:12:49 +02:00
|
|
|
use crate::utils::{uuid_from_duration, uuid_to_gid_u32, GraphemeClusterIter};
|
2022-10-01 08:08:51 +02:00
|
|
|
|
2019-09-06 05:04:58 +02:00
|
|
|
#[test]
|
|
|
|
fn test_utils_uuid_from_duration() {
|
2020-01-09 11:07:14 +01:00
|
|
|
let u1 = uuid_from_duration(Duration::from_secs(1), [0xff; 4]);
|
2019-09-06 05:04:58 +02:00
|
|
|
assert_eq!(
|
|
|
|
"00000000-0000-0001-0000-0000ffffffff",
|
2022-04-27 05:35:26 +02:00
|
|
|
u1.as_hyphenated().to_string()
|
2019-09-06 05:04:58 +02:00
|
|
|
);
|
|
|
|
|
2020-01-09 11:07:14 +01:00
|
|
|
let u2 = uuid_from_duration(Duration::from_secs(1000), [0xff; 4]);
|
2019-09-06 05:04:58 +02:00
|
|
|
assert_eq!(
|
|
|
|
"00000000-0000-03e8-0000-0000ffffffff",
|
2022-04-27 05:35:26 +02:00
|
|
|
u2.as_hyphenated().to_string()
|
2019-09-06 05:04:58 +02:00
|
|
|
);
|
|
|
|
}
|
2019-11-30 00:39:31 +01:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_utils_uuid_to_gid_u32() {
|
2022-12-18 04:26:20 +01:00
|
|
|
let u1 = uuid!("00000000-0000-0001-0000-000000000000");
|
2022-05-24 02:49:34 +02:00
|
|
|
let r1 = uuid_to_gid_u32(u1);
|
2024-08-26 01:30:20 +02:00
|
|
|
assert_eq!(r1, 0);
|
2019-11-30 00:39:31 +01:00
|
|
|
|
2022-12-18 04:26:20 +01:00
|
|
|
let u2 = uuid!("00000000-0000-0001-0000-0000ffffffff");
|
2022-05-24 02:49:34 +02:00
|
|
|
let r2 = uuid_to_gid_u32(u2);
|
2024-08-26 01:30:20 +02:00
|
|
|
assert_eq!(r2, 0xffffffff);
|
2019-11-30 00:39:31 +01:00
|
|
|
|
2022-12-18 04:26:20 +01:00
|
|
|
let u3 = uuid!("00000000-0000-0001-0000-ffff12345678");
|
2022-05-24 02:49:34 +02:00
|
|
|
let r3 = uuid_to_gid_u32(u3);
|
2024-08-26 01:30:20 +02:00
|
|
|
assert_eq!(r3, 0x12345678);
|
2019-11-30 00:39:31 +01:00
|
|
|
}
|
2024-07-20 05:12:49 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_utils_grapheme_cluster_iter() {
|
|
|
|
let d = "❤️🧡💛💚💙💜";
|
|
|
|
|
|
|
|
let gc_expect = vec!["❤", "\u{fe0f}", "🧡", "💛", "💚", "💙", "💜"];
|
|
|
|
let gc: Vec<_> = GraphemeClusterIter::new(d, 1).collect();
|
|
|
|
assert_eq!(gc, gc_expect);
|
|
|
|
|
|
|
|
let gc_expect = vec!["❤\u{fe0f}", "\u{fe0f}🧡", "🧡💛", "💛💚", "💚💙", "💙💜"];
|
|
|
|
let gc: Vec<_> = GraphemeClusterIter::new(d, 2).collect();
|
|
|
|
assert_eq!(gc, gc_expect);
|
|
|
|
|
|
|
|
let gc_expect = vec!["❤\u{fe0f}🧡", "\u{fe0f}🧡💛", "🧡💛💚", "💛💚💙", "💚💙💜"];
|
|
|
|
let gc: Vec<_> = GraphemeClusterIter::new(d, 3).collect();
|
|
|
|
assert_eq!(gc, gc_expect);
|
|
|
|
|
|
|
|
let d = "🤷🏿♂️";
|
|
|
|
|
|
|
|
let gc_expect = vec!["🤷", "🏿", "\u{200d}", "♂", "\u{fe0f}"];
|
|
|
|
let gc: Vec<_> = GraphemeClusterIter::new(d, 1).collect();
|
|
|
|
assert_eq!(gc, gc_expect);
|
|
|
|
|
|
|
|
let gc_expect = vec!["🤷🏿", "🏿\u{200d}", "\u{200d}♂", "♂\u{fe0f}"];
|
|
|
|
let gc: Vec<_> = GraphemeClusterIter::new(d, 2).collect();
|
|
|
|
assert_eq!(gc, gc_expect);
|
|
|
|
|
|
|
|
let gc_expect = vec!["🤷🏿\u{200d}", "🏿\u{200d}♂", "\u{200d}♂\u{fe0f}"];
|
|
|
|
let gc: Vec<_> = GraphemeClusterIter::new(d, 3).collect();
|
|
|
|
assert_eq!(gc, gc_expect);
|
|
|
|
}
|
2019-09-06 05:04:58 +02:00
|
|
|
}
|