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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use std::collections::HashMap;

use crate::web::Result;
use crate::db::invalid;
use crate::models::{DateTime, BigInt, DataType};

const ENCODE: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_";

thread_local! {
    static DECODE: HashMap<u8, i64> = {
        let mut hm = HashMap::new();
        let mut count = 0;
        for b in ENCODE {
            hm.insert(*b, count);
            count += 1;
        }
        hm
    }
}

pub fn encode(bi: BigInt) -> String {
    let mut n = bi.into();
    let mut v = vec![];
    loop {
        v.push(ENCODE[(n%64) as usize]);
        n /= 64;
        if n == 0 {
            break;
        }
    }
    v.reverse();
    String::from_utf8(v).unwrap()
}

pub fn decode(s: &str) -> Result<BigInt> {
    let mut n = 0;
    let mut m = 1;
    let mut c = 0;
    for b in s.as_bytes().iter().rev() {
        DECODE.with(|d| match d.get(b) {
            Some(o) => {
                n += o * m;
                Ok(())
            },
            None => return Err(invalid()),
        })?;
        c += 1;
        if c < s.len() {
            m *= 64;
        }
    }
    Ok(BigInt::from_val(n)?)
}

pub fn ago(dt: DateTime) -> String {
    let now = DateTime::now();
    let year_diff = now.date.year() as i32 - dt.date.year() as i32;
    let month_diff = now.date.month() as i32 - dt.date.month() as i32;
    let day_diff = now.date.day() as i32 - dt.date.day() as i32;
    if year_diff > 0 && month_diff >= 0 && day_diff >= 0 {
        plural(year_diff, "year")
    } else if month_diff > 1 && day_diff >= 0 {
        plural(month_diff, "month")
    } else if day_diff > 0 {
        plural(day_diff, "day")
    } else {
        let hour_diff = now.time.hour() as i32 - dt.time.hour() as i32;
        let minute_diff = now.time.minute() as i32 - dt.time.minute() as i32;
        if hour_diff > 1 {
            plural(hour_diff, "hour")
        } else if hour_diff == 1 {
            if minute_diff >= 0 {
                plural(hour_diff, "hour")
            } else {
                plural(hour_diff * 60 + minute_diff, "minute")
            }
        } else if minute_diff > 0 {
            plural(minute_diff, "minute")
        } else {
            "just now".to_string()
        }
    }
}

fn plural(n: i32, unit: &str) -> String {
    if n == 1 {
        if unit == "hour" {
            "an hour ago".to_string()
        } else {
            format!("a {} ago", unit)
        }
    } else {
        format!("{} {}s ago", n, unit)
    }
}

pub fn capitalize(s: &str) -> String {
    let mut c = s.chars();
    c.next().unwrap().to_uppercase().collect::<String>() + c.as_str()
}