misskey_api/model/id/
object_id.rs

1use std::fmt::{self, Display};
2use std::str::FromStr;
3
4use chrono::{DateTime, TimeZone, Utc};
5use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
6use thiserror::Error;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub struct ObjectId {
10    pub timestamp: u32,
11    pub random: u64,
12}
13
14impl ObjectId {
15    pub fn datetime(&self) -> DateTime<Utc> {
16        Utc.timestamp_millis(self.timestamp as i64 * 1000)
17    }
18}
19
20#[derive(Debug, Error, Clone)]
21#[error("invalid object id")]
22pub struct ParseObjectIdError {
23    _priv: (),
24}
25
26impl FromStr for ObjectId {
27    type Err = ParseObjectIdError;
28
29    fn from_str(s: &str) -> Result<ObjectId, Self::Err> {
30        let (timestamp_str, random_str) = s.split_at(s.len() - 16);
31
32        let timestamp = match u32::from_str_radix(timestamp_str, 16) {
33            Ok(x) => x,
34            Err(_) => return Err(ParseObjectIdError { _priv: () }),
35        };
36        let random = match u64::from_str_radix(random_str, 16) {
37            Ok(x) => x,
38            Err(_) => return Err(ParseObjectIdError { _priv: () }),
39        };
40
41        Ok(ObjectId { timestamp, random })
42    }
43}
44
45impl Display for ObjectId {
46    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
47        write!(f, "{:08x}{:016x}", self.timestamp, self.random)
48    }
49}
50
51impl Serialize for ObjectId {
52    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
53    where
54        S: Serializer,
55    {
56        serializer.collect_str(self)
57    }
58}
59
60impl<'de> Deserialize<'de> for ObjectId {
61    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
62    where
63        D: Deserializer<'de>,
64    {
65        String::deserialize(deserializer)?
66            .parse()
67            .map_err(de::Error::custom)
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::ObjectId;
74    use chrono::{DateTime, Duration, TimeZone, Utc};
75    use rand::{self, Rng};
76
77    fn new() -> ObjectId {
78        from_datetime(Utc::now())
79    }
80
81    fn from_datetime<Tz>(datetime: DateTime<Tz>) -> ObjectId
82    where
83        Tz: TimeZone,
84    {
85        from_datetime_with_source(datetime, &mut rand::thread_rng())
86    }
87
88    fn from_datetime_with_source<Tz, R>(datetime: DateTime<Tz>, source: &mut R) -> ObjectId
89    where
90        Tz: TimeZone,
91        R: Rng,
92    {
93        let timestamp = (datetime.timestamp_millis() as f64 / 1000.0).floor() as u32;
94        let random = source.gen::<u64>();
95        ObjectId { timestamp, random }
96    }
97
98    #[test]
99    fn test_deserialize_const() {
100        let string1 = "5f8b0eb37844631f2660354b";
101        let meid: ObjectId = string1.parse().expect("failed to parse");
102        assert_eq!(meid.datetime(), Utc.timestamp_millis(1602948787000));
103    }
104
105    #[test]
106    fn test_serialize_deserialize() {
107        let meid1 = new();
108        let string = meid1.to_string();
109        let meid2: ObjectId = string.parse().expect("failed to parse");
110        assert_eq!(meid1, meid2);
111    }
112
113    #[test]
114    fn test_deserialize_serialize() {
115        let string1 = "5f8b0eb37844631f2660354b";
116        let meid: ObjectId = string1.parse().expect("failed to parse");
117        let string2 = meid.to_string();
118        assert_eq!(string1, string2);
119    }
120
121    #[test]
122    fn test_order() {
123        let time = Utc::now();
124        let meid1 = from_datetime(time);
125        let meid2 = from_datetime(time + Duration::seconds(1));
126        assert!(meid1 < meid2);
127    }
128}