apalis_core/task/
task_id.rs1use std::{
6 fmt::{Debug, Display},
7 hash::Hash,
8 str::FromStr,
9};
10
11use crate::{
12 task::{data::MissingDataError, Task},
13 task_fn::FromRequest,
14};
15
16pub use random_id::RandomId;
17
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, PartialOrd, Ord)]
21pub struct TaskId<IdType = RandomId>(IdType);
22
23impl<IdType> TaskId<IdType> {
24 pub fn new(id: IdType) -> Self {
26 Self(id)
27 }
28 pub fn inner(&self) -> &IdType {
30 &self.0
31 }
32}
33
34#[derive(Debug, thiserror::Error)]
36pub enum TaskIdError<E> {
37 #[error("could not decode task_id: `{0}`")]
39 Decode(E),
40}
41
42impl<IdType: FromStr> FromStr for TaskId<IdType> {
43 type Err = TaskIdError<IdType::Err>;
44 fn from_str(s: &str) -> Result<Self, Self::Err> {
45 Ok(TaskId::new(
46 IdType::from_str(s).map_err(|e| TaskIdError::Decode(e))?,
47 ))
48 }
49}
50
51impl<IdType: FromStr> TryFrom<&'_ str> for TaskId<IdType> {
52 type Error = TaskIdError<IdType::Err>;
53
54 fn try_from(value: &'_ str) -> Result<Self, Self::Error> {
55 Self::from_str(value)
56 }
57}
58
59impl<IdType: Display> Display for TaskId<IdType> {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 Display::fmt(&self.0, f)
62 }
63}
64
65impl<Args: Sync, Ctx: Sync, IdType: Sync + Send + Clone> FromRequest<Task<Args, Ctx, IdType>>
66 for TaskId<IdType>
67{
68 type Error = MissingDataError;
69 async fn from_request(task: &Task<Args, Ctx, IdType>) -> Result<Self, Self::Error> {
70 Ok(task
71 .parts
72 .task_id
73 .clone()
74 .ok_or(MissingDataError::NotFound(
75 std::any::type_name::<TaskId<IdType>>().to_string(),
76 ))?)
77 }
78}
79
80mod random_id {
81 use super::*;
82 use std::convert::Infallible;
83 use std::sync::atomic::{AtomicU64, Ordering};
84 use std::time::{SystemTime, UNIX_EPOCH};
85
86 const ALPHABET: &[u8] = b"abcdefghijkmnopqrstuvwxyz23456789-";
87 const BASE: u64 = 34;
88 const TIME_LEN: usize = 6;
89 const RANDOM_LEN: usize = 5;
90
91 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
96 #[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
97 pub struct RandomId(String);
98
99 impl FromStr for RandomId {
100 type Err = Infallible;
101 fn from_str(s: &str) -> Result<Self, Self::Err> {
102 Ok(RandomId(s.to_owned()))
103 }
104 }
105
106 impl TryFrom<&'_ str> for RandomId {
107 type Error = Infallible;
108
109 fn try_from(value: &'_ str) -> Result<Self, Self::Error> {
110 Self::from_str(value)
111 }
112 }
113
114 impl Display for RandomId {
115 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116 Display::fmt(&self.0, f)
117 }
118 }
119
120 impl Default for RandomId {
121 fn default() -> Self {
122 RandomId(unique_id())
123 }
124 }
125
126 static COUNTER: AtomicU64 = AtomicU64::new(0);
128
129 fn encode_base64(mut value: u64, length: usize) -> String {
131 let mut buf = vec![b'A'; length];
132 for i in (0..length).rev() {
133 buf[i] = ALPHABET[(value % BASE) as usize];
134 value /= BASE;
135 }
136 String::from_utf8(buf).unwrap()
137 }
138
139 pub(super) fn unique_id() -> String {
141 let timestamp = current_time_millis();
142 let time_str = encode_base64(timestamp, TIME_LEN);
143
144 let count = COUNTER.fetch_add(1, Ordering::Relaxed);
146 let rand_part = encode_base64(xorshift64(timestamp ^ count), RANDOM_LEN);
147
148 format!("{time_str}{rand_part}")
149 }
150
151 fn current_time_millis() -> u64 {
153 SystemTime::now()
154 .duration_since(UNIX_EPOCH)
155 .unwrap_or_default()
156 .as_millis() as u64
157 }
158
159 fn xorshift64(mut x: u64) -> u64 {
161 x ^= x << 13;
162 x ^= x >> 7;
163 x ^= x << 17;
164 x
165 }
166}