jacquard_api/network_slices/actor/
profile.rs1#[allow(unused_imports)]
9use alloc::collections::BTreeMap;
10
11#[allow(unused_imports)]
12use core::marker::PhantomData;
13use jacquard_common::CowStr;
14
15#[allow(unused_imports)]
16use jacquard_common::deps::codegen::unicode_segmentation::UnicodeSegmentation;
17use jacquard_common::types::blob::BlobRef;
18use jacquard_common::types::collection::{Collection, RecordError};
19use jacquard_common::types::string::{AtUri, Cid, Datetime};
20use jacquard_common::types::uri::{RecordUri, UriError};
21use jacquard_common::xrpc::XrpcResp;
22use jacquard_derive::{IntoStatic, lexicon};
23use jacquard_lexicon::lexicon::LexiconDoc;
24use jacquard_lexicon::schema::LexiconSchema;
25
26#[allow(unused_imports)]
27use jacquard_lexicon::validation::{ConstraintError, ValidationPath};
28use serde::{Serialize, Deserialize};
29#[lexicon]
32#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
33#[serde(
34 rename_all = "camelCase",
35 rename = "network.slices.actor.profile",
36 tag = "$type"
37)]
38pub struct Profile<'a> {
39 #[serde(skip_serializing_if = "Option::is_none")]
41 #[serde(borrow)]
42 pub avatar: Option<BlobRef<'a>>,
43 #[serde(skip_serializing_if = "Option::is_none")]
44 pub created_at: Option<Datetime>,
45 #[serde(skip_serializing_if = "Option::is_none")]
47 #[serde(borrow)]
48 pub description: Option<CowStr<'a>>,
49 #[serde(skip_serializing_if = "Option::is_none")]
50 #[serde(borrow)]
51 pub display_name: Option<CowStr<'a>>,
52}
53
54#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
57#[serde(rename_all = "camelCase")]
58pub struct ProfileGetRecordOutput<'a> {
59 #[serde(skip_serializing_if = "Option::is_none")]
60 #[serde(borrow)]
61 pub cid: Option<Cid<'a>>,
62 #[serde(borrow)]
63 pub uri: AtUri<'a>,
64 #[serde(borrow)]
65 pub value: Profile<'a>,
66}
67
68impl<'a> Profile<'a> {
69 pub fn uri(
70 uri: impl Into<CowStr<'a>>,
71 ) -> Result<RecordUri<'a, ProfileRecord>, UriError> {
72 RecordUri::try_from_uri(AtUri::new_cow(uri.into())?)
73 }
74}
75
76#[derive(Debug, Serialize, Deserialize)]
79pub struct ProfileRecord;
80impl XrpcResp for ProfileRecord {
81 const NSID: &'static str = "network.slices.actor.profile";
82 const ENCODING: &'static str = "application/json";
83 type Output<'de> = ProfileGetRecordOutput<'de>;
84 type Err<'de> = RecordError<'de>;
85}
86
87impl From<ProfileGetRecordOutput<'_>> for Profile<'_> {
88 fn from(output: ProfileGetRecordOutput<'_>) -> Self {
89 use jacquard_common::IntoStatic;
90 output.value.into_static()
91 }
92}
93
94impl Collection for Profile<'_> {
95 const NSID: &'static str = "network.slices.actor.profile";
96 type Record = ProfileRecord;
97}
98
99impl Collection for ProfileRecord {
100 const NSID: &'static str = "network.slices.actor.profile";
101 type Record = ProfileRecord;
102}
103
104impl<'a> LexiconSchema for Profile<'a> {
105 fn nsid() -> &'static str {
106 "network.slices.actor.profile"
107 }
108 fn def_name() -> &'static str {
109 "main"
110 }
111 fn lexicon_doc() -> LexiconDoc<'static> {
112 lexicon_doc_network_slices_actor_profile()
113 }
114 fn validate(&self) -> Result<(), ConstraintError> {
115 if let Some(ref value) = self.avatar {
116 {
117 let size = value.blob().size;
118 if size > 1000000usize {
119 return Err(ConstraintError::BlobTooLarge {
120 path: ValidationPath::from_field("avatar"),
121 max: 1000000usize,
122 actual: size,
123 });
124 }
125 }
126 }
127 if let Some(ref value) = self.avatar {
128 {
129 let mime = value.blob().mime_type.as_str();
130 let accepted: &[&str] = &["image/png", "image/jpeg"];
131 let matched = accepted
132 .iter()
133 .any(|pattern| {
134 if *pattern == "*/*" {
135 true
136 } else if pattern.ends_with("/*") {
137 let prefix = &pattern[..pattern.len() - 2];
138 mime.starts_with(prefix)
139 && mime.as_bytes().get(prefix.len()) == Some(&b'/')
140 } else {
141 mime == *pattern
142 }
143 });
144 if !matched {
145 return Err(ConstraintError::BlobMimeTypeNotAccepted {
146 path: ValidationPath::from_field("avatar"),
147 accepted: vec![
148 "image/png".to_string(), "image/jpeg".to_string()
149 ],
150 actual: mime.to_string(),
151 });
152 }
153 }
154 }
155 if let Some(ref value) = self.description {
156 #[allow(unused_comparisons)]
157 if <str>::len(value.as_ref()) > 2560usize {
158 return Err(ConstraintError::MaxLength {
159 path: ValidationPath::from_field("description"),
160 max: 2560usize,
161 actual: <str>::len(value.as_ref()),
162 });
163 }
164 }
165 if let Some(ref value) = self.description {
166 {
167 let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
168 if count > 256usize {
169 return Err(ConstraintError::MaxGraphemes {
170 path: ValidationPath::from_field("description"),
171 max: 256usize,
172 actual: count,
173 });
174 }
175 }
176 }
177 if let Some(ref value) = self.display_name {
178 #[allow(unused_comparisons)]
179 if <str>::len(value.as_ref()) > 640usize {
180 return Err(ConstraintError::MaxLength {
181 path: ValidationPath::from_field("display_name"),
182 max: 640usize,
183 actual: <str>::len(value.as_ref()),
184 });
185 }
186 }
187 if let Some(ref value) = self.display_name {
188 {
189 let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
190 if count > 64usize {
191 return Err(ConstraintError::MaxGraphemes {
192 path: ValidationPath::from_field("display_name"),
193 max: 64usize,
194 actual: count,
195 });
196 }
197 }
198 }
199 Ok(())
200 }
201}
202
203pub mod profile_state {
204
205 pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
206 #[allow(unused)]
207 use ::core::marker::PhantomData;
208 mod sealed {
209 pub trait Sealed {}
210 }
211 pub trait State: sealed::Sealed {}
213 pub struct Empty(());
215 impl sealed::Sealed for Empty {}
216 impl State for Empty {}
217 #[allow(non_camel_case_types)]
219 pub mod members {}
220}
221
222pub struct ProfileBuilder<'a, S: profile_state::State> {
224 _state: PhantomData<fn() -> S>,
225 _fields: (
226 Option<BlobRef<'a>>,
227 Option<Datetime>,
228 Option<CowStr<'a>>,
229 Option<CowStr<'a>>,
230 ),
231 _lifetime: PhantomData<&'a ()>,
232}
233
234impl<'a> Profile<'a> {
235 pub fn new() -> ProfileBuilder<'a, profile_state::Empty> {
237 ProfileBuilder::new()
238 }
239}
240
241impl<'a> ProfileBuilder<'a, profile_state::Empty> {
242 pub fn new() -> Self {
244 ProfileBuilder {
245 _state: PhantomData,
246 _fields: (None, None, None, None),
247 _lifetime: PhantomData,
248 }
249 }
250}
251
252impl<'a, S: profile_state::State> ProfileBuilder<'a, S> {
253 pub fn avatar(mut self, value: impl Into<Option<BlobRef<'a>>>) -> Self {
255 self._fields.0 = value.into();
256 self
257 }
258 pub fn maybe_avatar(mut self, value: Option<BlobRef<'a>>) -> Self {
260 self._fields.0 = value;
261 self
262 }
263}
264
265impl<'a, S: profile_state::State> ProfileBuilder<'a, S> {
266 pub fn created_at(mut self, value: impl Into<Option<Datetime>>) -> Self {
268 self._fields.1 = value.into();
269 self
270 }
271 pub fn maybe_created_at(mut self, value: Option<Datetime>) -> Self {
273 self._fields.1 = value;
274 self
275 }
276}
277
278impl<'a, S: profile_state::State> ProfileBuilder<'a, S> {
279 pub fn description(mut self, value: impl Into<Option<CowStr<'a>>>) -> Self {
281 self._fields.2 = value.into();
282 self
283 }
284 pub fn maybe_description(mut self, value: Option<CowStr<'a>>) -> Self {
286 self._fields.2 = value;
287 self
288 }
289}
290
291impl<'a, S: profile_state::State> ProfileBuilder<'a, S> {
292 pub fn display_name(mut self, value: impl Into<Option<CowStr<'a>>>) -> Self {
294 self._fields.3 = value.into();
295 self
296 }
297 pub fn maybe_display_name(mut self, value: Option<CowStr<'a>>) -> Self {
299 self._fields.3 = value;
300 self
301 }
302}
303
304impl<'a, S> ProfileBuilder<'a, S>
305where
306 S: profile_state::State,
307{
308 pub fn build(self) -> Profile<'a> {
310 Profile {
311 avatar: self._fields.0,
312 created_at: self._fields.1,
313 description: self._fields.2,
314 display_name: self._fields.3,
315 extra_data: Default::default(),
316 }
317 }
318 pub fn build_with_data(
320 self,
321 extra_data: BTreeMap<
322 jacquard_common::deps::smol_str::SmolStr,
323 jacquard_common::types::value::Data<'a>,
324 >,
325 ) -> Profile<'a> {
326 Profile {
327 avatar: self._fields.0,
328 created_at: self._fields.1,
329 description: self._fields.2,
330 display_name: self._fields.3,
331 extra_data: Some(extra_data),
332 }
333 }
334}
335
336fn lexicon_doc_network_slices_actor_profile() -> LexiconDoc<'static> {
337 #[allow(unused_imports)]
338 use jacquard_common::{CowStr, deps::smol_str::SmolStr, types::blob::MimeType};
339 use jacquard_lexicon::lexicon::*;
340 use alloc::collections::BTreeMap;
341 LexiconDoc {
342 lexicon: Lexicon::Lexicon1,
343 id: CowStr::new_static("network.slices.actor.profile"),
344 defs: {
345 let mut map = BTreeMap::new();
346 map.insert(
347 SmolStr::new_static("main"),
348 LexUserType::Record(LexRecord {
349 description: Some(
350 CowStr::new_static("A declaration of a basic account profile."),
351 ),
352 key: Some(CowStr::new_static("literal:self")),
353 record: LexRecordRecord::Object(LexObject {
354 properties: {
355 #[allow(unused_mut)]
356 let mut map = BTreeMap::new();
357 map.insert(
358 SmolStr::new_static("avatar"),
359 LexObjectProperty::Blob(LexBlob { ..Default::default() }),
360 );
361 map.insert(
362 SmolStr::new_static("createdAt"),
363 LexObjectProperty::String(LexString {
364 format: Some(LexStringFormat::Datetime),
365 ..Default::default()
366 }),
367 );
368 map.insert(
369 SmolStr::new_static("description"),
370 LexObjectProperty::String(LexString {
371 description: Some(
372 CowStr::new_static("Free-form profile description text."),
373 ),
374 max_length: Some(2560usize),
375 max_graphemes: Some(256usize),
376 ..Default::default()
377 }),
378 );
379 map.insert(
380 SmolStr::new_static("displayName"),
381 LexObjectProperty::String(LexString {
382 max_length: Some(640usize),
383 max_graphemes: Some(64usize),
384 ..Default::default()
385 }),
386 );
387 map
388 },
389 ..Default::default()
390 }),
391 ..Default::default()
392 }),
393 );
394 map
395 },
396 ..Default::default()
397 }
398}