Skip to main content

haste_jwt/
lib.rs

1use derivative::Derivative;
2use haste_fhir_model::r4::generated::terminology::{BoundCode, UserRole as FHIRUserRole};
3use serde::{Deserialize, Serialize};
4use std::fmt::Display;
5
6#[cfg(feature = "reflect")]
7pub mod reflect;
8
9#[cfg(feature = "sqlx")]
10pub mod sqlx;
11
12pub mod claims;
13pub mod scopes;
14
15#[cfg(test)]
16mod proptests;
17
18// Reserved keyword for system tenant, author and project.
19static SYSTEM: &str = "system";
20
21/// The FHIR versions a project can be served as.
22///
23/// A project's version is fixed when it is created and cannot be changed
24/// afterwards, so it is safe to cache and to carry in an access token.
25#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Deserialize, Serialize)]
26#[cfg_attr(feature = "sqlx", derive(::sqlx::Type))]
27#[cfg_attr(
28    feature = "sqlx",
29    sqlx(type_name = "fhir_version", rename_all = "lowercase")
30)]
31#[serde(rename_all = "lowercase")]
32pub enum SupportedFHIRVersions {
33    #[default]
34    R4,
35}
36
37impl SupportedFHIRVersions {
38    /// Every version a project can be served as, in the order routes are
39    /// registered for them.
40    pub const ALL: &[SupportedFHIRVersions] = &[SupportedFHIRVersions::R4];
41
42    /// The version as it appears in a URL.
43    #[must_use]
44    pub fn as_str(&self) -> &'static str {
45        match self {
46            SupportedFHIRVersions::R4 => "r4",
47        }
48    }
49
50    /// The version a URL segment names, if it names one at all. Used to tell a
51    /// leading version segment apart from a resource type.
52    #[must_use]
53    pub fn from_url_segment(segment: &str) -> Option<SupportedFHIRVersions> {
54        SupportedFHIRVersions::ALL
55            .iter()
56            .find(|version| version.as_str() == segment)
57            .cloned()
58    }
59}
60
61impl Display for SupportedFHIRVersions {
62    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63        write!(f, "{}", self.as_str())
64    }
65}
66
67#[derive(Clone, Debug, PartialEq, PartialOrd, Deserialize, Serialize)]
68#[serde(rename_all = "lowercase")]
69pub enum UserRole {
70    Owner,
71    Admin,
72    Member,
73}
74
75impl From<BoundCode<FHIRUserRole>> for UserRole {
76    fn from(role: BoundCode<FHIRUserRole>) -> Self {
77        if role == FHIRUserRole::owner() {
78            UserRole::Owner
79        } else if role == FHIRUserRole::admin() {
80            UserRole::Admin
81        } else {
82            UserRole::Member
83        }
84    }
85}
86
87#[derive(Derivative, Clone)]
88#[derivative(Debug = "transparent")]
89pub enum AuthorId {
90    // System is used for system level actions such as tenant creation etc..
91    System,
92    User(ResourceId),
93}
94
95impl AuthorId {
96    #[must_use]
97    pub fn new(id: String) -> Self {
98        // Should never be able to create a system author from user.
99        if id == SYSTEM {
100            AuthorId::System
101        } else {
102            AuthorId::User(ResourceId::new(id))
103        }
104    }
105}
106
107impl AsRef<str> for AuthorId {
108    fn as_ref(&self) -> &str {
109        match self {
110            AuthorId::System => SYSTEM,
111            AuthorId::User(id) => id.as_ref(),
112        }
113    }
114}
115
116impl<'de> Deserialize<'de> for AuthorId {
117    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
118    where
119        D: serde::Deserializer<'de>,
120    {
121        Ok(AuthorId::new(String::deserialize(deserializer)?))
122    }
123}
124
125impl Serialize for AuthorId {
126    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
127    where
128        S: serde::Serializer,
129    {
130        serializer.serialize_str(self.as_ref())
131    }
132}
133
134impl Display for AuthorId {
135    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136        match self {
137            AuthorId::System => write!(f, "{SYSTEM}"),
138            AuthorId::User(id) => write!(f, "{}", id.as_ref()),
139        }
140    }
141}
142
143#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
144pub enum AuthorKind {
145    System,
146    Membership,
147    ClientApplication,
148    OperationDefinition,
149}
150
151impl AsRef<str> for AuthorKind {
152    fn as_ref(&self) -> &str {
153        match self {
154            AuthorKind::System => "System",
155            AuthorKind::Membership => "Membership",
156            AuthorKind::ClientApplication => "ClientApplication",
157            AuthorKind::OperationDefinition => "OperationDefinition",
158        }
159    }
160}
161
162impl Display for AuthorKind {
163    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164        write!(f, "{}", self.as_ref())
165    }
166}
167
168#[derive(Derivative, Clone, PartialEq, Eq, Hash)]
169#[derivative(Debug = "transparent")]
170pub enum TenantId {
171    System,
172    #[derivative(Debug = "transparent")]
173    Custom(String),
174}
175
176impl TenantId {
177    #[must_use]
178    pub fn new(id: String) -> Self {
179        // Should never be able to create a system tenant from user.
180        if id == SYSTEM {
181            TenantId::System
182        } else {
183            TenantId::Custom(id)
184        }
185    }
186}
187
188impl From<String> for TenantId {
189    fn from(id: String) -> Self {
190        TenantId::new(id)
191    }
192}
193
194impl AsRef<str> for TenantId {
195    fn as_ref(&self) -> &str {
196        match self {
197            TenantId::System => SYSTEM,
198            TenantId::Custom(id) => id,
199        }
200    }
201}
202
203impl<'de> Deserialize<'de> for TenantId {
204    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
205    where
206        D: serde::Deserializer<'de>,
207    {
208        Ok(TenantId::new(String::deserialize(deserializer)?))
209    }
210}
211
212impl Serialize for TenantId {
213    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
214    where
215        S: serde::Serializer,
216    {
217        serializer.serialize_str(self.as_ref())
218    }
219}
220
221impl Display for TenantId {
222    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
223        match self {
224            TenantId::System => write!(f, "{SYSTEM}"),
225            TenantId::Custom(id) => write!(f, "{id}"),
226        }
227    }
228}
229
230#[derive(Derivative, Clone, PartialEq, Eq, Hash)]
231#[derivative(Debug = "transparent")]
232pub enum ProjectId {
233    System,
234    #[derivative(Debug = "transparent")]
235    Custom(String),
236}
237impl ProjectId {
238    #[must_use]
239    pub fn new(id: String) -> Self {
240        // Should never be able to create a system project from user.
241        if id == SYSTEM {
242            ProjectId::System
243        } else {
244            ProjectId::Custom(id)
245        }
246    }
247}
248
249impl AsRef<str> for ProjectId {
250    fn as_ref(&self) -> &str {
251        match self {
252            ProjectId::System => SYSTEM,
253            ProjectId::Custom(id) => id,
254        }
255    }
256}
257
258impl<'de> Deserialize<'de> for ProjectId {
259    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
260    where
261        D: serde::Deserializer<'de>,
262    {
263        Ok(ProjectId::new(String::deserialize(deserializer)?))
264    }
265}
266
267impl Serialize for ProjectId {
268    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
269    where
270        S: serde::Serializer,
271    {
272        serializer.serialize_str(self.as_ref())
273    }
274}
275
276impl Display for ProjectId {
277    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278        match self {
279            ProjectId::System => write!(f, "{SYSTEM}"),
280            ProjectId::Custom(id) => write!(f, "{id}"),
281        }
282    }
283}
284
285pub struct VersionIdRef<'a>(&'a str);
286impl<'a> VersionIdRef<'a> {
287    #[must_use]
288    pub fn new(id: &'a str) -> Self {
289        VersionIdRef(id)
290    }
291}
292impl<'a> AsRef<str> for VersionIdRef<'a> {
293    fn as_ref(&self) -> &'a str {
294        self.0
295    }
296}
297impl<'a> From<&'a VersionId> for VersionIdRef<'a> {
298    fn from(version_id: &'a VersionId) -> Self {
299        VersionIdRef::new(&version_id.0)
300    }
301}
302
303#[derive(Derivative, Deserialize, Serialize, Clone, PartialEq, Eq, Hash)]
304#[derivative(Debug = "transparent")]
305pub struct VersionId(String);
306impl VersionId {
307    #[must_use]
308    pub fn new(id: String) -> Self {
309        VersionId(id)
310    }
311}
312impl From<String> for VersionId {
313    fn from(id: String) -> Self {
314        VersionId(id)
315    }
316}
317impl AsRef<str> for VersionId {
318    fn as_ref(&self) -> &str {
319        &self.0
320    }
321}
322
323#[derive(Derivative, Clone, Deserialize, Serialize)]
324#[derivative(Debug = "transparent")]
325pub struct ResourceId(String);
326impl ResourceId {
327    #[must_use]
328    pub fn new(id: String) -> Self {
329        ResourceId(id)
330    }
331}
332impl AsRef<str> for ResourceId {
333    fn as_ref(&self) -> &str {
334        &self.0
335    }
336}