s2_common/types/
access.rs

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
150impl From<AccessTokenId> for AccessTokenIdStartAfter {
151    fn from(value: AccessTokenId) -> Self {
152        Self(value.0, PhantomData)
153    }
154}
155
156#[derive(Debug, Hash, EnumSetType, strum::EnumCount)]
157pub enum Operation {
158    ListBasins = 1,
159    CreateBasin = 2,
160    DeleteBasin = 3,
161    ReconfigureBasin = 4,
162    GetBasinConfig = 5,
163    IssueAccessToken = 6,
164    RevokeAccessToken = 7,
165    ListAccessTokens = 8,
166    ListStreams = 9,
167    CreateStream = 10,
168    DeleteStream = 11,
169    GetStreamConfig = 12,
170    ReconfigureStream = 13,
171    CheckTail = 14,
172    Append = 15,
173    Read = 16,
174    Trim = 17,
175    Fence = 18,
176    AccountMetrics = 19,
177    BasinMetrics = 20,
178    StreamMetrics = 21,
179}
180
181#[derive(Debug, Clone, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
182#[cfg_attr(
183    feature = "rkyv",
184    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
185)]
186pub enum ResourceSet<E, P> {
187    #[default]
188    None,
189    Exact(E),
190    Prefix(P),
191}
192
193pub type BasinResourceSet = ResourceSet<BasinName, BasinNamePrefix>;
194pub type StreamResourceSet = ResourceSet<StreamName, StreamNamePrefix>;
195pub type AccessTokenResourceSet = ResourceSet<AccessTokenId, AccessTokenIdPrefix>;
196
197#[derive(Debug, Clone, Copy, Default)]
198pub struct ReadWritePermissions {
199    pub read: bool,
200    pub write: bool,
201}
202
203#[derive(Debug, Clone, Default)]
204pub struct PermittedOperationGroups {
205    pub account: ReadWritePermissions,
206    pub basin: ReadWritePermissions,
207    pub stream: ReadWritePermissions,
208}
209
210#[derive(Debug, Clone, Default)]
211pub struct AccessTokenScope {
212    pub basins: BasinResourceSet,
213    pub streams: StreamResourceSet,
214    pub access_tokens: AccessTokenResourceSet,
215    pub op_groups: PermittedOperationGroups,
216    pub ops: EnumSet<Operation>,
217}
218
219#[derive(Debug, Clone)]
220pub struct AccessTokenInfo {
221    pub id: AccessTokenId,
222    pub expires_at: time::OffsetDateTime,
223    pub auto_prefix_streams: bool,
224    pub scope: AccessTokenScope,
225}
226
227#[derive(Debug, Clone)]
228pub struct IssueAccessTokenRequest {
229    pub id: AccessTokenId,
230    pub expires_at: Option<time::OffsetDateTime>,
231    pub auto_prefix_streams: bool,
232    pub scope: AccessTokenScope,
233}
234
235pub type ListAccessTokensRequest = ListItemsRequest<AccessTokenIdPrefix, AccessTokenIdStartAfter>;