misskey_api/model/id/
object_id.rs1use 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}