1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//! Various small utilities accumulated over time for the WebPush server
use std::collections::HashMap;
use std::hash::Hash;
use std::time::Duration;

use base64::Engine;
use serde::{Deserialize, Deserializer};

pub mod timing;
pub mod user_agent;

pub use self::timing::{ms_since_epoch, ms_utc_midnight, sec_since_epoch, us_since_epoch};

pub const ONE_DAY_IN_SECONDS: u64 = 60 * 60 * 24;

pub trait InsertOpt<K: Eq + Hash, V> {
    /// Insert an item only if it exists
    fn insert_opt(&mut self, key: impl Into<K>, value: Option<impl Into<V>>);
}

impl<K: Eq + Hash, V> InsertOpt<K, V> for HashMap<K, V> {
    fn insert_opt(&mut self, key: impl Into<K>, value: Option<impl Into<V>>) {
        if let Some(value) = value {
            self.insert(key.into(), value.into());
        }
    }
}

/// Convenience wrapper for base64 decoding
/// *note* The `base64` devs are HIGHLY opinionated and the method to encode/decode
/// changes frequently. This function encapsulates that as much as possible.
pub fn b64_decode_url(input: &str) -> Result<Vec<u8>, base64::DecodeError> {
    base64::engine::general_purpose::URL_SAFE_NO_PAD.decode(input.trim_end_matches('='))
}

pub fn b64_encode_url(input: &Vec<u8>) -> String {
    base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(input)
}

pub fn b64_decode_std(input: &str) -> Result<Vec<u8>, base64::DecodeError> {
    base64::engine::general_purpose::STANDARD_NO_PAD.decode(input.trim_end_matches('='))
}

pub fn b64_encode_std(input: &Vec<u8>) -> String {
    base64::engine::general_purpose::STANDARD_NO_PAD.encode(input)
}

pub fn deserialize_u32_to_duration<'de, D>(deserializer: D) -> Result<Duration, D::Error>
where
    D: Deserializer<'de>,
{
    let seconds: u32 = Deserialize::deserialize(deserializer)?;
    Ok(Duration::from_secs(seconds.into()))
}

pub fn deserialize_opt_u32_to_duration<'de, D>(
    deserializer: D,
) -> Result<Option<Duration>, D::Error>
where
    D: Deserializer<'de>,
{
    let seconds: Option<u32> = Deserialize::deserialize(deserializer)?;
    Ok(seconds.map(|v| Duration::from_secs(v.into())))
}