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
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>;