1
//! String libraries.
2

            
3
use chrono::{DateTime, SecondsFormat, Utc};
4
use hex;
5
use hmac::Hmac;
6
use pbkdf2;
7
use rand::{distr::Alphanumeric, Rng};
8
use regex::Regex;
9
use sha2::{Digest, Sha256};
10
use url::Url;
11

            
12
const PASSWORD_ROUNDS: u32 = 10000;
13

            
14
/// To transfer hex address string to 128-bit integer.
15
10
pub fn hex_addr_to_u128(addr: &str) -> Result<u128, &'static str> {
16
10
    if addr.len() == 0 || addr.len() > 32 || addr.len() % 2 != 0 {
17
6
        return Err("invalid address format");
18
4
    }
19
4

            
20
4
    match u128::from_str_radix(addr, 16) {
21
2
        Err(_) => Err("invalid address format"),
22
2
        Ok(value) => Ok(value),
23
    }
24
10
}
25

            
26
/// To check if the account is valid.
27
8
pub fn is_account(account: &str) -> bool {
28
8
    let name_regex = Regex::new(r"^[a-z0-9]{1}[a-z0-9_-]*$").unwrap();
29
8
    let email_regex = Regex::new(
30
8
        r"^([a-z0-9_+]([a-z0-9_+.]*[a-z0-9_+])?)@([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6})$",
31
8
    )
32
8
    .unwrap();
33
8

            
34
8
    name_regex.is_match(account) || email_regex.is_match(account)
35
8
}
36

            
37
/// To check if the (unit/application/network) code is valid.
38
4
pub fn is_code(code: &str) -> bool {
39
4
    let regex = Regex::new(r"^[a-z0-9]{1}[a-z0-9_-]*$").unwrap();
40
4
    regex.is_match(code)
41
4
}
42

            
43
/// To check if the (client) scope is valid.
44
4
pub fn is_scope(scope: &str) -> bool {
45
4
    let regex = Regex::new(r"^[a-z0-9]+([\.]{1}[a-z0-9]+)*$").unwrap();
46
4
    regex.is_match(scope)
47
4
}
48

            
49
/// To check if the (redirect) URI is valid.
50
4
pub fn is_uri(uri: &str) -> bool {
51
4
    Url::parse(uri).is_ok()
52
4
}
53

            
54
/// To hash the password.
55
2
pub fn password_hash(password: &str, salt: &str) -> String {
56
2
    let mut res: [u8; 32] = [0; 32];
57
2
    let _ = pbkdf2::pbkdf2::<Hmac<Sha256>>(
58
2
        password.as_bytes(),
59
2
        salt.as_bytes(),
60
2
        PASSWORD_ROUNDS,
61
2
        &mut res,
62
2
    );
63
2
    hex::encode(res)
64
2
}
65

            
66
/// To generate item ID in `[timestamp-milliseconds]-[random-alphanumeric]` format.
67
8
pub fn random_id(time: &DateTime<Utc>, len: usize) -> String {
68
8
    format!("{}-{}", time.timestamp_millis(), randomstring(len))
69
8
}
70

            
71
/// To generate hex-string item ID using [`random_id`] and additional hash.
72
4
pub fn random_id_sha(time: &DateTime<Utc>, len: usize) -> String {
73
4
    let str = random_id(time, len);
74
4
    let mut hasher = Sha256::new();
75
4
    hasher.update(str.as_bytes());
76
4
    hex::encode(hasher.finalize())
77
4
}
78

            
79
/// To generate random alphanumeric string with the specified length.
80
12
pub fn randomstring(len: usize) -> String {
81
12
    let mut rng = rand::rng();
82
12
    std::iter::repeat(())
83
120
        .map(|()| rng.sample(Alphanumeric))
84
12
        .map(char::from)
85
12
        .take(len)
86
12
        .collect()
87
12
}
88

            
89
/// To convert time to RFC 3339 format with milliseconds precision (`YYYY-MM-DDThh:mm:ss.SSSZ`).
90
2
pub fn time_str(time: &DateTime<Utc>) -> String {
91
2
    time.to_rfc3339_opts(SecondsFormat::Millis, true)
92
2
}
93

            
94
/// To generate hex address string with the specified length (hex string length).
95
32
pub fn u128_to_addr(value: u128, len: usize) -> String {
96
32
    match len {
97
2
        0 | 1 | 2 => format!("{:02x}", value & 0xff),
98
2
        3 | 4 => format!("{:04x}", value & 0xffff),
99
2
        5 | 6 => format!("{:06x}", value & 0xff_ffff),
100
2
        7 | 8 => format!("{:08x}", value & 0xffff_ffff),
101
2
        9 | 10 => format!("{:010x}", value & 0xff_ffff_ffff),
102
2
        11 | 12 => format!("{:012x}", value & 0xffff_ffff_ffff),
103
2
        13 | 14 => format!("{:014x}", value & 0xff_ffff_ffff_ffff),
104
2
        15 | 16 => format!("{:016x}", value & 0xffff_ffff_ffff_ffff),
105
2
        17 | 18 => format!("{:018x}", value & 0xff_ffff_ffff_ffff_ffff),
106
2
        19 | 20 => format!("{:020x}", value & 0xffff_ffff_ffff_ffff_ffff),
107
2
        21 | 22 => format!("{:022x}", value & 0xff_ffff_ffff_ffff_ffff_ffff),
108
2
        23 | 24 => format!("{:024x}", value & 0xffff_ffff_ffff_ffff_ffff_ffff),
109
2
        25 | 26 => format!("{:026x}", value & 0xff_ffff_ffff_ffff_ffff_ffff_ffff),
110
2
        27 | 28 => format!("{:028x}", value & 0xffff_ffff_ffff_ffff_ffff_ffff_ffff),
111
2
        29 | 30 => format!("{:030x}", value & 0xff_ffff_ffff_ffff_ffff_ffff_ffff_ffff),
112
2
        _ => format!("{:032x}", value),
113
    }
114
32
}