misskey_api/model/id/
meid.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 Meid {
10 pub timestamp: i64,
11 pub random: u64,
13}
14
15impl Meid {
16 pub fn datetime(&self) -> DateTime<Utc> {
17 Utc.timestamp_millis(self.timestamp)
18 }
19}
20
21const TIME_OFFSET: i64 = 0x800000000000;
23
24#[derive(Debug, Error, Clone)]
25#[error("invalid meid")]
26pub struct ParseMeidError {
27 _priv: (),
28}
29
30impl FromStr for Meid {
31 type Err = ParseMeidError;
32
33 fn from_str(s: &str) -> Result<Meid, Self::Err> {
34 let (timestamp_str, random_str) = s.split_at(s.len() - 12);
35
36 let timestamp = match i64::from_str_radix(timestamp_str, 16) {
37 Ok(0) => 0,
38 Ok(x) => x - TIME_OFFSET,
39 Err(_) => return Err(ParseMeidError { _priv: () }),
40 };
41
42 let random = match u64::from_str_radix(random_str, 16) {
43 Ok(x) => x,
44 Err(_) => return Err(ParseMeidError { _priv: () }),
45 };
46
47 Ok(Meid { timestamp, random })
48 }
49}
50
51impl Display for Meid {
52 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53 let with_offset = if self.timestamp <= 0 {
54 0
55 } else {
56 self.timestamp + TIME_OFFSET
57 };
58 write!(
59 f,
60 "{:012x}{:012x}",
61 with_offset,
62 self.random % 2_u64.pow(48)
63 )
64 }
65}
66
67impl Serialize for Meid {
68 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
69 where
70 S: Serializer,
71 {
72 serializer.collect_str(self)
73 }
74}
75
76impl<'de> Deserialize<'de> for Meid {
77 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
78 where
79 D: Deserializer<'de>,
80 {
81 String::deserialize(deserializer)?
82 .parse()
83 .map_err(de::Error::custom)
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::Meid;
90 use chrono::{DateTime, Duration, TimeZone, Utc};
91 use rand::{self, Rng};
92
93 fn new() -> Meid {
94 from_datetime(Utc::now())
95 }
96
97 fn from_datetime<Tz>(datetime: DateTime<Tz>) -> Meid
98 where
99 Tz: TimeZone,
100 {
101 from_datetime_with_source(datetime, &mut rand::thread_rng())
102 }
103
104 fn from_datetime_with_source<Tz, R>(datetime: DateTime<Tz>, source: &mut R) -> Meid
105 where
106 Tz: TimeZone,
107 R: Rng,
108 {
109 let timestamp = datetime.timestamp_millis();
110 let random = source.gen::<u64>() % 2_u64.pow(48);
111 Meid { timestamp, random }
112 }
113
114 #[test]
115 fn test_deserialize_const() {
116 let string = "817537316bb2ef661de6af11";
117 let meid: Meid = string.parse().expect("failed to parse");
118 assert_eq!(meid.datetime(), Utc.timestamp_millis(1602948787122));
119 }
120
121 #[test]
122 fn test_serialize_deserialize() {
123 let meid1 = new();
124 let string = meid1.to_string();
125 let meid2: Meid = string.parse().expect("failed to parse");
126 assert_eq!(meid1, meid2);
127 }
128
129 #[test]
130 fn test_deserialize_serialize() {
131 let string1 = "817537316bb2ef661de6af11";
132 let meid: Meid = string1.parse().expect("failed to parse");
133 let string2 = meid.to_string();
134 assert_eq!(string1, string2);
135 }
136
137 #[test]
138 fn test_order() {
139 let time = Utc::now();
140 let meid1 = from_datetime(time);
141 let meid2 = from_datetime(time + Duration::milliseconds(1));
142 assert!(meid1 < meid2);
143 }
144}