1use crate::ScimEntryHeader;
2use base64urlsafedata::Base64UrlSafeData;
3use std::fmt;
4use url::Url;
5use uuid::Uuid;
6
7use serde::{Deserialize, Serialize};
8use serde_with::skip_serializing_none;
9
10#[skip_serializing_none]
11#[derive(Serialize, Deserialize, Debug, Clone)]
12#[serde(rename_all = "camelCase")]
13pub struct Name {
14 formatted: Option<String>,
16 family_name: Option<String>,
17 given_name: Option<String>,
18 middle_name: Option<String>,
19 honorific_prefix: Option<String>,
20 honorific_suffix: Option<String>,
21}
22
23#[allow(non_camel_case_types)]
36#[derive(Serialize, Deserialize, Debug, Clone)]
37pub enum Locale {
38 en,
39 #[serde(rename = "en-AU")]
40 en_AU,
41 #[serde(rename = "en-US")]
42 en_US,
43 de,
44 #[serde(rename = "de-DE")]
45 de_DE,
46}
47
48impl fmt::Display for Locale {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 match self {
51 Locale::en => write!(f, "en"),
52 Locale::en_AU => write!(f, "en-AU"),
53 Locale::en_US => write!(f, "en-US"),
54 Locale::de => write!(f, "de"),
55 Locale::de_DE => write!(f, "de-DE"),
56 }
57 }
58}
59
60#[allow(non_camel_case_types)]
61#[derive(Serialize, Deserialize, Debug, Clone)]
62pub enum Timezone {
63 #[serde(rename = "Australia/Brisbane")]
64 australia_brisbane,
65 #[serde(rename = "America/Los_Angeles")]
66 america_los_angeles,
67}
68
69impl fmt::Display for Timezone {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 match self {
72 Timezone::australia_brisbane => write!(f, "Australia/Brisbane"),
73 Timezone::america_los_angeles => write!(f, "America/Los_Angeles"),
74 }
75 }
76}
77
78#[skip_serializing_none]
79#[derive(Serialize, Deserialize, Debug, Clone, Default)]
80#[serde(rename_all = "camelCase")]
81pub struct MultiValueAttr {
82 #[serde(rename = "type")]
83 pub type_: Option<String>,
84 pub primary: Option<bool>,
85 pub display: Option<String>,
86 #[serde(rename = "$ref")]
87 pub ref_: Option<Url>,
88 pub value: String,
89}
90
91#[skip_serializing_none]
92#[derive(Serialize, Deserialize, Debug, Clone)]
93#[serde(rename_all = "camelCase")]
94pub struct Photo {
95 #[serde(rename = "type")]
96 type_: Option<String>,
97 primary: Option<bool>,
98 display: Option<String>,
99 #[serde(rename = "$ref")]
100 ref_: Option<Url>,
101 value: Url,
102}
103
104#[skip_serializing_none]
105#[derive(Serialize, Deserialize, Debug, Clone)]
106pub struct Binary {
107 #[serde(rename = "type")]
108 type_: Option<String>,
109 primary: Option<bool>,
110 display: Option<String>,
111 #[serde(rename = "$ref")]
112 ref_: Option<Url>,
113 value: Base64UrlSafeData,
114}
115
116#[skip_serializing_none]
117#[derive(Serialize, Deserialize, Debug, Clone)]
118#[serde(rename_all = "camelCase")]
119pub struct Address {
120 #[serde(rename = "type")]
121 type_: Option<String>,
122 primary: Option<bool>,
123 formatted: Option<String>,
124 street_address: Option<String>,
125 locality: Option<String>,
126 region: Option<String>,
127 postal_code: Option<String>,
128 country: Option<String>,
129}
130
131#[skip_serializing_none]
140#[derive(Serialize, Deserialize, Debug, Clone)]
141#[serde(rename_all = "camelCase")]
142pub struct Group {
143 #[serde(rename = "type")]
144 type_: Option<String>,
145 #[serde(rename = "$ref")]
146 ref_: Url,
147 value: Uuid,
148 display: String,
149}
150
151#[skip_serializing_none]
152#[derive(Serialize, Deserialize, Debug, Clone)]
153#[serde(rename_all = "camelCase")]
154pub struct User {
155 #[serde(flatten)]
156 entry: ScimEntryHeader,
157 user_name: String,
159 name: Option<Name>,
161 display_name: Option<String>,
163 nick_name: Option<String>,
164 profile_url: Option<Url>,
165 title: Option<String>,
166 user_type: Option<String>,
167 preferred_language: Option<Locale>,
168 locale: Option<Locale>,
169 timezone: Option<Timezone>,
172 active: bool,
173 password: Option<String>,
174 #[serde(default, skip_serializing_if = "Vec::is_empty")]
175 emails: Vec<MultiValueAttr>,
176 #[serde(default, skip_serializing_if = "Vec::is_empty")]
177 phone_numbers: Vec<MultiValueAttr>,
178 #[serde(default, skip_serializing_if = "Vec::is_empty")]
179 ims: Vec<MultiValueAttr>,
180 #[serde(default, skip_serializing_if = "Vec::is_empty")]
181 photos: Vec<Photo>,
182 #[serde(default, skip_serializing_if = "Vec::is_empty")]
183 addresses: Vec<Address>,
184 #[serde(default, skip_serializing_if = "Vec::is_empty")]
185 groups: Vec<Group>,
186 #[serde(default, skip_serializing_if = "Vec::is_empty")]
187 entitlements: Vec<MultiValueAttr>,
188 #[serde(default, skip_serializing_if = "Vec::is_empty")]
189 roles: Vec<MultiValueAttr>,
190 #[serde(default, skip_serializing_if = "Vec::is_empty")]
191 x509certificates: Vec<Binary>,
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197 use crate::constants::RFC7643_USER;
198
199 #[test]
200 fn parse_user() {
201 let _ = tracing_subscriber::fmt::try_init();
202
203 let u: User = serde_json::from_str(RFC7643_USER).expect("Failed to parse RFC7643_USER");
204
205 tracing::trace!(?u);
206
207 let s = serde_json::to_string_pretty(&u).expect("Failed to serialise RFC7643_USER");
208 eprintln!("{}", s);
209 }
210}