Skip to main content

pgtask_core/
identifier.rs

1use std::{fmt, num::NonZeroU32, str::FromStr};
2
3use serde::{Deserialize, Serialize};
4use thiserror::Error;
5use uuid::Uuid;
6
7const MAX_QUEUE_NAME_BYTES: usize = 128;
8const MAX_TASK_NAME_BYTES: usize = 255;
9
10#[derive(Clone, Debug, Error, Eq, PartialEq)]
11pub enum NameError {
12    #[error("{kind} must not be empty")]
13    Empty { kind: &'static str },
14    #[error("{kind} must be at most {maximum} bytes, got {actual}")]
15    TooLong {
16        kind: &'static str,
17        maximum: usize,
18        actual: usize,
19    },
20    #[error("{kind} contains unsupported character {character:?}")]
21    UnsupportedCharacter { kind: &'static str, character: char },
22}
23
24fn validate_name(value: &str, kind: &'static str, maximum: usize) -> Result<(), NameError> {
25    if value.is_empty() {
26        return Err(NameError::Empty { kind });
27    }
28    if value.len() > maximum {
29        return Err(NameError::TooLong {
30            kind,
31            maximum,
32            actual: value.len(),
33        });
34    }
35    if let Some(character) = value
36        .chars()
37        .find(|character| !(character.is_ascii_alphanumeric() || matches!(character, '.' | '_' | ':' | '-')))
38    {
39        return Err(NameError::UnsupportedCharacter { kind, character });
40    }
41    Ok(())
42}
43
44macro_rules! name_type {
45    ($name:ident, $kind:literal, $maximum:expr) => {
46        #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
47        #[serde(try_from = "String", into = "String")]
48        pub struct $name(String);
49
50        impl $name {
51            pub fn new(value: impl Into<String>) -> Result<Self, NameError> {
52                let value = value.into();
53                validate_name(&value, $kind, $maximum)?;
54                Ok(Self(value))
55            }
56
57            pub fn as_str(&self) -> &str {
58                &self.0
59            }
60        }
61
62        impl AsRef<str> for $name {
63            fn as_ref(&self) -> &str {
64                self.as_str()
65            }
66        }
67
68        impl fmt::Display for $name {
69            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
70                formatter.write_str(self.as_str())
71            }
72        }
73
74        impl TryFrom<String> for $name {
75            type Error = NameError;
76
77            fn try_from(value: String) -> Result<Self, Self::Error> {
78                Self::new(value)
79            }
80        }
81
82        impl From<$name> for String {
83            fn from(value: $name) -> Self {
84                value.0
85            }
86        }
87    };
88}
89
90name_type!(QueueName, "queue name", MAX_QUEUE_NAME_BYTES);
91name_type!(ScheduleName, "schedule name", MAX_TASK_NAME_BYTES);
92name_type!(SignalName, "signal name", MAX_TASK_NAME_BYTES);
93name_type!(StepName, "step name", MAX_TASK_NAME_BYTES);
94name_type!(TaskName, "task name", MAX_TASK_NAME_BYTES);
95
96impl Default for QueueName {
97    fn default() -> Self {
98        Self("default".to_owned())
99    }
100}
101
102macro_rules! uuid_type {
103    ($name:ident) => {
104        #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
105        #[serde(transparent)]
106        pub struct $name(Uuid);
107
108        impl $name {
109            pub fn new() -> Self {
110                Self(Uuid::now_v7())
111            }
112
113            pub const fn from_uuid(value: Uuid) -> Self {
114                Self(value)
115            }
116
117            pub const fn as_uuid(self) -> Uuid {
118                self.0
119            }
120        }
121
122        impl Default for $name {
123            fn default() -> Self {
124                Self::new()
125            }
126        }
127
128        impl fmt::Display for $name {
129            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
130                self.0.fmt(formatter)
131            }
132        }
133
134        impl FromStr for $name {
135            type Err = uuid::Error;
136
137            fn from_str(value: &str) -> Result<Self, Self::Err> {
138                Uuid::parse_str(value).map(Self)
139            }
140        }
141
142        impl From<$name> for Uuid {
143            fn from(value: $name) -> Self {
144                value.0
145            }
146        }
147    };
148}
149
150uuid_type!(TaskId);
151uuid_type!(ScheduleId);
152uuid_type!(WorkerId);
153uuid_type!(LeaseToken);
154
155#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
156#[serde(transparent)]
157pub struct HandlerVersion(NonZeroU32);
158
159impl HandlerVersion {
160    pub const fn new(value: NonZeroU32) -> Self {
161        Self(value)
162    }
163
164    pub const fn get(self) -> u32 {
165        self.0.get()
166    }
167}
168
169impl Default for HandlerVersion {
170    fn default() -> Self {
171        Self(NonZeroU32::MIN)
172    }
173}