1use std::{marker::PhantomData, ops::Deref, str::FromStr};
2
3use compact_str::{CompactString, ToCompactString};
4use enumset::{EnumSet, EnumSetType};
5
6use super::{
7 ValidationError,
8 basin::{BasinName, BasinNamePrefix},
9 stream::{StreamName, StreamNamePrefix},
10 strings::{IdProps, PrefixProps, StartAfterProps, StrProps},
11};
12use crate::{caps, types::resources::ListItemsRequest};
13
14#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
15#[cfg_attr(
16 feature = "rkyv",
17 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
18)]
19pub struct AccessTokenIdStr<T: StrProps>(CompactString, PhantomData<T>);
20
21impl<T: StrProps> AccessTokenIdStr<T> {
22 fn validate_str(id: &str) -> Result<(), ValidationError> {
23 if !T::IS_PREFIX && id.is_empty() {
24 return Err(format!("access token {} must not be empty", T::FIELD_NAME).into());
25 }
26
27 if id.len() > caps::MAX_ACCESS_TOKEN_ID_LEN {
28 return Err(format!(
29 "access token {} must not exceed {} bytes in length",
30 T::FIELD_NAME,
31 caps::MAX_ACCESS_TOKEN_ID_LEN
32 )
33 .into());
34 }
35
36 Ok(())
37 }
38}
39
40#[cfg(feature = "utoipa")]
41impl<T> utoipa::PartialSchema for AccessTokenIdStr<T>
42where
43 T: StrProps,
44{
45 fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
46 utoipa::openapi::Object::builder()
47 .schema_type(utoipa::openapi::Type::String)
48 .min_length((!T::IS_PREFIX).then_some(1))
49 .max_length(Some(caps::MAX_ACCESS_TOKEN_ID_LEN))
50 .into()
51 }
52}
53
54#[cfg(feature = "utoipa")]
55impl<T> utoipa::ToSchema for AccessTokenIdStr<T> where T: StrProps {}
56
57impl<T: StrProps> serde::Serialize for AccessTokenIdStr<T> {
58 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
59 where
60 S: serde::Serializer,
61 {
62 serializer.serialize_str(&self.0)
63 }
64}
65
66impl<'de, T: StrProps> serde::Deserialize<'de> for AccessTokenIdStr<T> {
67 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
68 where
69 D: serde::Deserializer<'de>,
70 {
71 let s = CompactString::deserialize(deserializer)?;
72 s.try_into().map_err(serde::de::Error::custom)
73 }
74}
75
76impl<T: StrProps> AsRef<str> for AccessTokenIdStr<T> {
77 fn as_ref(&self) -> &str {
78 &self.0
79 }
80}
81
82impl<T: StrProps> Deref for AccessTokenIdStr<T> {
83 type Target = str;
84
85 fn deref(&self) -> &Self::Target {
86 &self.0
87 }
88}
89
90impl<T: StrProps> TryFrom<CompactString> for AccessTokenIdStr<T> {
91 type Error = ValidationError;
92
93 fn try_from(name: CompactString) -> Result<Self, Self::Error> {
94 Self::validate_str(&name)?;
95 Ok(Self(name, PhantomData))
96 }
97}
98
99impl<T: StrProps> FromStr for AccessTokenIdStr<T> {
100 type Err = ValidationError;
101
102 fn from_str(s: &str) -> Result<Self, Self::Err> {
103 Self::validate_str(s)?;
104 Ok(Self(s.to_compact_string(), PhantomData))
105 }
106}
107
108impl<T: StrProps> std::fmt::Debug for AccessTokenIdStr<T> {
109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 f.write_str(&self.0)
111 }
112}
113
114impl<T: StrProps> std::fmt::Display for AccessTokenIdStr<T> {
115 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116 f.write_str(&self.0)
117 }
118}
119
120impl<T: StrProps> From<AccessTokenIdStr<T>> for CompactString {
121 fn from(value: AccessTokenIdStr<T>) -> Self {
122 value.0
123 }
124}
125
126pub type AccessTokenId = AccessTokenIdStr<IdProps>;
127
128pub type AccessTokenIdPrefix = AccessTokenIdStr<PrefixProps>;
129
130impl Default for AccessTokenIdPrefix {
131 fn default() -> Self {
132 AccessTokenIdStr(CompactString::default(), PhantomData)
133 }
134}
135
136impl From<AccessTokenId> for AccessTokenIdPrefix {
137 fn from(value: AccessTokenId) -> Self {
138 Self(value.0, PhantomData)
139 }
140}
141
142pub type AccessTokenIdStartAfter = AccessTokenIdStr<StartAfterProps>;
143
144impl Default for AccessTokenIdStartAfter {
145 fn default() -> Self {
146 AccessTokenIdStr(CompactString::default(), PhantomData)
147 }
148}
149
150#[derive(Debug, Hash, EnumSetType, strum::EnumCount)]
151pub enum Operation {
152 ListBasins = 1,
153 CreateBasin = 2,
154 DeleteBasin = 3,
155 ReconfigureBasin = 4,
156 GetBasinConfig = 5,
157 IssueAccessToken = 6,
158 RevokeAccessToken = 7,
159 ListAccessTokens = 8,
160 ListStreams = 9,
161 CreateStream = 10,
162 DeleteStream = 11,
163 GetStreamConfig = 12,
164 ReconfigureStream = 13,
165 CheckTail = 14,
166 Append = 15,
167 Read = 16,
168 Trim = 17,
169 Fence = 18,
170 AccountMetrics = 19,
171 BasinMetrics = 20,
172 StreamMetrics = 21,
173}
174
175#[derive(Debug, Clone, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
176#[cfg_attr(
177 feature = "rkyv",
178 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
179)]
180pub enum ResourceSet<E, P> {
181 #[default]
182 None,
183 Exact(E),
184 Prefix(P),
185}
186
187pub type BasinResourceSet = ResourceSet<BasinName, BasinNamePrefix>;
188pub type StreamResourceSet = ResourceSet<StreamName, StreamNamePrefix>;
189pub type AccessTokenResourceSet = ResourceSet<AccessTokenId, AccessTokenIdPrefix>;
190
191#[derive(Debug, Clone, Copy, Default)]
192pub struct ReadWritePermissions {
193 pub read: bool,
194 pub write: bool,
195}
196
197#[derive(Debug, Clone, Default)]
198pub struct PermittedOperationGroups {
199 pub account: ReadWritePermissions,
200 pub basin: ReadWritePermissions,
201 pub stream: ReadWritePermissions,
202}
203
204#[derive(Debug, Clone, Default)]
205pub struct AccessTokenScope {
206 pub basins: BasinResourceSet,
207 pub streams: StreamResourceSet,
208 pub access_tokens: AccessTokenResourceSet,
209 pub op_groups: PermittedOperationGroups,
210 pub ops: EnumSet<Operation>,
211}
212
213#[derive(Debug, Clone)]
214pub struct AccessTokenInfo {
215 pub id: AccessTokenId,
216 pub expires_at: time::OffsetDateTime,
217 pub auto_prefix_streams: bool,
218 pub scope: AccessTokenScope,
219}
220
221#[derive(Debug, Clone)]
222pub struct IssueAccessTokenRequest {
223 pub id: AccessTokenId,
224 pub expires_at: Option<time::OffsetDateTime>,
225 pub auto_prefix_streams: bool,
226 pub scope: AccessTokenScope,
227}
228
229pub type ListAccessTokensRequest = ListItemsRequest<AccessTokenIdPrefix, AccessTokenIdStartAfter>;