1
//! String libraries.
2

            
3
use chrono::{DateTime, SecondsFormat, Utc};
4
use hex;
5
use hmac::Hmac;
6
use pbkdf2;
7
use rand::{distributions::Alphanumeric, thread_rng, 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
5
pub fn hex_addr_to_u128(addr: &str) -> Result<u128, &'static str> {
16
5
    if addr.len() == 0 || addr.len() > 32 || addr.len() % 2 != 0 {
17
3
        return Err("invalid address format");
18
2
    }
19
2

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

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

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

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

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

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

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

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

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

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

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

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