Skip to main content

jacquard_api/sh_tangled/actor/
profile.rs

1// @generated by jacquard-lexicon. DO NOT EDIT.
2//
3// Lexicon: sh.tangled.actor.profile
4//
5// This file was automatically generated from Lexicon schemas.
6// Any manual changes will be overwritten on the next regeneration.
7
8#[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, UriValue};
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/// A declaration of a Tangled account profile.
30
31#[lexicon]
32#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
33#[serde(rename_all = "camelCase", rename = "sh.tangled.actor.profile", tag = "$type")]
34pub struct Profile<'a> {
35    ///Small image to be displayed next to posts from account. AKA, 'profile picture'
36    #[serde(skip_serializing_if = "Option::is_none")]
37    #[serde(borrow)]
38    pub avatar: Option<BlobRef<'a>>,
39    ///Include link to this account on Bluesky.
40    pub bluesky: bool,
41    ///Free-form profile description text.
42    #[serde(skip_serializing_if = "Option::is_none")]
43    #[serde(borrow)]
44    pub description: Option<CowStr<'a>>,
45    #[serde(skip_serializing_if = "Option::is_none")]
46    #[serde(borrow)]
47    pub links: Option<Vec<UriValue<'a>>>,
48    ///Free-form location text.
49    #[serde(skip_serializing_if = "Option::is_none")]
50    #[serde(borrow)]
51    pub location: Option<CowStr<'a>>,
52    ///Any ATURI, it is up to appviews to validate these fields.
53    #[serde(skip_serializing_if = "Option::is_none")]
54    #[serde(borrow)]
55    pub pinned_repositories: Option<Vec<AtUri<'a>>>,
56    ///Preferred gender pronouns.
57    #[serde(skip_serializing_if = "Option::is_none")]
58    #[serde(borrow)]
59    pub pronouns: Option<CowStr<'a>>,
60    #[serde(skip_serializing_if = "Option::is_none")]
61    #[serde(borrow)]
62    pub stats: Option<Vec<CowStr<'a>>>,
63}
64
65/// Typed wrapper for GetRecord response with this collection's record type.
66
67#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
68#[serde(rename_all = "camelCase")]
69pub struct ProfileGetRecordOutput<'a> {
70    #[serde(skip_serializing_if = "Option::is_none")]
71    #[serde(borrow)]
72    pub cid: Option<Cid<'a>>,
73    #[serde(borrow)]
74    pub uri: AtUri<'a>,
75    #[serde(borrow)]
76    pub value: Profile<'a>,
77}
78
79impl<'a> Profile<'a> {
80    pub fn uri(
81        uri: impl Into<CowStr<'a>>,
82    ) -> Result<RecordUri<'a, ProfileRecord>, UriError> {
83        RecordUri::try_from_uri(AtUri::new_cow(uri.into())?)
84    }
85}
86
87/// Marker type for deserializing records from this collection.
88
89#[derive(Debug, Serialize, Deserialize)]
90pub struct ProfileRecord;
91impl XrpcResp for ProfileRecord {
92    const NSID: &'static str = "sh.tangled.actor.profile";
93    const ENCODING: &'static str = "application/json";
94    type Output<'de> = ProfileGetRecordOutput<'de>;
95    type Err<'de> = RecordError<'de>;
96}
97
98impl From<ProfileGetRecordOutput<'_>> for Profile<'_> {
99    fn from(output: ProfileGetRecordOutput<'_>) -> Self {
100        use jacquard_common::IntoStatic;
101        output.value.into_static()
102    }
103}
104
105impl Collection for Profile<'_> {
106    const NSID: &'static str = "sh.tangled.actor.profile";
107    type Record = ProfileRecord;
108}
109
110impl Collection for ProfileRecord {
111    const NSID: &'static str = "sh.tangled.actor.profile";
112    type Record = ProfileRecord;
113}
114
115impl<'a> LexiconSchema for Profile<'a> {
116    fn nsid() -> &'static str {
117        "sh.tangled.actor.profile"
118    }
119    fn def_name() -> &'static str {
120        "main"
121    }
122    fn lexicon_doc() -> LexiconDoc<'static> {
123        lexicon_doc_sh_tangled_actor_profile()
124    }
125    fn validate(&self) -> Result<(), ConstraintError> {
126        if let Some(ref value) = self.avatar {
127            {
128                let size = value.blob().size;
129                if size > 1000000usize {
130                    return Err(ConstraintError::BlobTooLarge {
131                        path: ValidationPath::from_field("avatar"),
132                        max: 1000000usize,
133                        actual: size,
134                    });
135                }
136            }
137        }
138        if let Some(ref value) = self.avatar {
139            {
140                let mime = value.blob().mime_type.as_str();
141                let accepted: &[&str] = &["image/png", "image/jpeg"];
142                let matched = accepted
143                    .iter()
144                    .any(|pattern| {
145                        if *pattern == "*/*" {
146                            true
147                        } else if pattern.ends_with("/*") {
148                            let prefix = &pattern[..pattern.len() - 2];
149                            mime.starts_with(prefix)
150                                && mime.as_bytes().get(prefix.len()) == Some(&b'/')
151                        } else {
152                            mime == *pattern
153                        }
154                    });
155                if !matched {
156                    return Err(ConstraintError::BlobMimeTypeNotAccepted {
157                        path: ValidationPath::from_field("avatar"),
158                        accepted: vec![
159                            "image/png".to_string(), "image/jpeg".to_string()
160                        ],
161                        actual: mime.to_string(),
162                    });
163                }
164            }
165        }
166        if let Some(ref value) = self.description {
167            #[allow(unused_comparisons)]
168            if <str>::len(value.as_ref()) > 2560usize {
169                return Err(ConstraintError::MaxLength {
170                    path: ValidationPath::from_field("description"),
171                    max: 2560usize,
172                    actual: <str>::len(value.as_ref()),
173                });
174            }
175        }
176        if let Some(ref value) = self.description {
177            {
178                let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
179                if count > 256usize {
180                    return Err(ConstraintError::MaxGraphemes {
181                        path: ValidationPath::from_field("description"),
182                        max: 256usize,
183                        actual: count,
184                    });
185                }
186            }
187        }
188        if let Some(ref value) = self.links {
189            #[allow(unused_comparisons)]
190            if value.len() > 5usize {
191                return Err(ConstraintError::MaxLength {
192                    path: ValidationPath::from_field("links"),
193                    max: 5usize,
194                    actual: value.len(),
195                });
196            }
197        }
198        if let Some(ref value) = self.links {
199            #[allow(unused_comparisons)]
200            if value.len() < 0usize {
201                return Err(ConstraintError::MinLength {
202                    path: ValidationPath::from_field("links"),
203                    min: 0usize,
204                    actual: value.len(),
205                });
206            }
207        }
208        if let Some(ref value) = self.location {
209            #[allow(unused_comparisons)]
210            if <str>::len(value.as_ref()) > 400usize {
211                return Err(ConstraintError::MaxLength {
212                    path: ValidationPath::from_field("location"),
213                    max: 400usize,
214                    actual: <str>::len(value.as_ref()),
215                });
216            }
217        }
218        if let Some(ref value) = self.location {
219            {
220                let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
221                if count > 40usize {
222                    return Err(ConstraintError::MaxGraphemes {
223                        path: ValidationPath::from_field("location"),
224                        max: 40usize,
225                        actual: count,
226                    });
227                }
228            }
229        }
230        if let Some(ref value) = self.pinned_repositories {
231            #[allow(unused_comparisons)]
232            if value.len() > 6usize {
233                return Err(ConstraintError::MaxLength {
234                    path: ValidationPath::from_field("pinned_repositories"),
235                    max: 6usize,
236                    actual: value.len(),
237                });
238            }
239        }
240        if let Some(ref value) = self.pinned_repositories {
241            #[allow(unused_comparisons)]
242            if value.len() < 0usize {
243                return Err(ConstraintError::MinLength {
244                    path: ValidationPath::from_field("pinned_repositories"),
245                    min: 0usize,
246                    actual: value.len(),
247                });
248            }
249        }
250        if let Some(ref value) = self.pronouns {
251            #[allow(unused_comparisons)]
252            if <str>::len(value.as_ref()) > 40usize {
253                return Err(ConstraintError::MaxLength {
254                    path: ValidationPath::from_field("pronouns"),
255                    max: 40usize,
256                    actual: <str>::len(value.as_ref()),
257                });
258            }
259        }
260        if let Some(ref value) = self.stats {
261            #[allow(unused_comparisons)]
262            if value.len() > 2usize {
263                return Err(ConstraintError::MaxLength {
264                    path: ValidationPath::from_field("stats"),
265                    max: 2usize,
266                    actual: value.len(),
267                });
268            }
269        }
270        if let Some(ref value) = self.stats {
271            #[allow(unused_comparisons)]
272            if value.len() < 0usize {
273                return Err(ConstraintError::MinLength {
274                    path: ValidationPath::from_field("stats"),
275                    min: 0usize,
276                    actual: value.len(),
277                });
278            }
279        }
280        Ok(())
281    }
282}
283
284pub mod profile_state {
285
286    pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
287    #[allow(unused)]
288    use ::core::marker::PhantomData;
289    mod sealed {
290        pub trait Sealed {}
291    }
292    /// State trait tracking which required fields have been set
293    pub trait State: sealed::Sealed {
294        type Bluesky;
295    }
296    /// Empty state - all required fields are unset
297    pub struct Empty(());
298    impl sealed::Sealed for Empty {}
299    impl State for Empty {
300        type Bluesky = Unset;
301    }
302    ///State transition - sets the `bluesky` field to Set
303    pub struct SetBluesky<S: State = Empty>(PhantomData<fn() -> S>);
304    impl<S: State> sealed::Sealed for SetBluesky<S> {}
305    impl<S: State> State for SetBluesky<S> {
306        type Bluesky = Set<members::bluesky>;
307    }
308    /// Marker types for field names
309    #[allow(non_camel_case_types)]
310    pub mod members {
311        ///Marker type for the `bluesky` field
312        pub struct bluesky(());
313    }
314}
315
316/// Builder for constructing an instance of this type
317pub struct ProfileBuilder<'a, S: profile_state::State> {
318    _state: PhantomData<fn() -> S>,
319    _fields: (
320        Option<BlobRef<'a>>,
321        Option<bool>,
322        Option<CowStr<'a>>,
323        Option<Vec<UriValue<'a>>>,
324        Option<CowStr<'a>>,
325        Option<Vec<AtUri<'a>>>,
326        Option<CowStr<'a>>,
327        Option<Vec<CowStr<'a>>>,
328    ),
329    _lifetime: PhantomData<&'a ()>,
330}
331
332impl<'a> Profile<'a> {
333    /// Create a new builder for this type
334    pub fn new() -> ProfileBuilder<'a, profile_state::Empty> {
335        ProfileBuilder::new()
336    }
337}
338
339impl<'a> ProfileBuilder<'a, profile_state::Empty> {
340    /// Create a new builder with all fields unset
341    pub fn new() -> Self {
342        ProfileBuilder {
343            _state: PhantomData,
344            _fields: (None, None, None, None, None, None, None, None),
345            _lifetime: PhantomData,
346        }
347    }
348}
349
350impl<'a, S: profile_state::State> ProfileBuilder<'a, S> {
351    /// Set the `avatar` field (optional)
352    pub fn avatar(mut self, value: impl Into<Option<BlobRef<'a>>>) -> Self {
353        self._fields.0 = value.into();
354        self
355    }
356    /// Set the `avatar` field to an Option value (optional)
357    pub fn maybe_avatar(mut self, value: Option<BlobRef<'a>>) -> Self {
358        self._fields.0 = value;
359        self
360    }
361}
362
363impl<'a, S> ProfileBuilder<'a, S>
364where
365    S: profile_state::State,
366    S::Bluesky: profile_state::IsUnset,
367{
368    /// Set the `bluesky` field (required)
369    pub fn bluesky(
370        mut self,
371        value: impl Into<bool>,
372    ) -> ProfileBuilder<'a, profile_state::SetBluesky<S>> {
373        self._fields.1 = Option::Some(value.into());
374        ProfileBuilder {
375            _state: PhantomData,
376            _fields: self._fields,
377            _lifetime: PhantomData,
378        }
379    }
380}
381
382impl<'a, S: profile_state::State> ProfileBuilder<'a, S> {
383    /// Set the `description` field (optional)
384    pub fn description(mut self, value: impl Into<Option<CowStr<'a>>>) -> Self {
385        self._fields.2 = value.into();
386        self
387    }
388    /// Set the `description` field to an Option value (optional)
389    pub fn maybe_description(mut self, value: Option<CowStr<'a>>) -> Self {
390        self._fields.2 = value;
391        self
392    }
393}
394
395impl<'a, S: profile_state::State> ProfileBuilder<'a, S> {
396    /// Set the `links` field (optional)
397    pub fn links(mut self, value: impl Into<Option<Vec<UriValue<'a>>>>) -> Self {
398        self._fields.3 = value.into();
399        self
400    }
401    /// Set the `links` field to an Option value (optional)
402    pub fn maybe_links(mut self, value: Option<Vec<UriValue<'a>>>) -> Self {
403        self._fields.3 = value;
404        self
405    }
406}
407
408impl<'a, S: profile_state::State> ProfileBuilder<'a, S> {
409    /// Set the `location` field (optional)
410    pub fn location(mut self, value: impl Into<Option<CowStr<'a>>>) -> Self {
411        self._fields.4 = value.into();
412        self
413    }
414    /// Set the `location` field to an Option value (optional)
415    pub fn maybe_location(mut self, value: Option<CowStr<'a>>) -> Self {
416        self._fields.4 = value;
417        self
418    }
419}
420
421impl<'a, S: profile_state::State> ProfileBuilder<'a, S> {
422    /// Set the `pinnedRepositories` field (optional)
423    pub fn pinned_repositories(
424        mut self,
425        value: impl Into<Option<Vec<AtUri<'a>>>>,
426    ) -> Self {
427        self._fields.5 = value.into();
428        self
429    }
430    /// Set the `pinnedRepositories` field to an Option value (optional)
431    pub fn maybe_pinned_repositories(mut self, value: Option<Vec<AtUri<'a>>>) -> Self {
432        self._fields.5 = value;
433        self
434    }
435}
436
437impl<'a, S: profile_state::State> ProfileBuilder<'a, S> {
438    /// Set the `pronouns` field (optional)
439    pub fn pronouns(mut self, value: impl Into<Option<CowStr<'a>>>) -> Self {
440        self._fields.6 = value.into();
441        self
442    }
443    /// Set the `pronouns` field to an Option value (optional)
444    pub fn maybe_pronouns(mut self, value: Option<CowStr<'a>>) -> Self {
445        self._fields.6 = value;
446        self
447    }
448}
449
450impl<'a, S: profile_state::State> ProfileBuilder<'a, S> {
451    /// Set the `stats` field (optional)
452    pub fn stats(mut self, value: impl Into<Option<Vec<CowStr<'a>>>>) -> Self {
453        self._fields.7 = value.into();
454        self
455    }
456    /// Set the `stats` field to an Option value (optional)
457    pub fn maybe_stats(mut self, value: Option<Vec<CowStr<'a>>>) -> Self {
458        self._fields.7 = value;
459        self
460    }
461}
462
463impl<'a, S> ProfileBuilder<'a, S>
464where
465    S: profile_state::State,
466    S::Bluesky: profile_state::IsSet,
467{
468    /// Build the final struct
469    pub fn build(self) -> Profile<'a> {
470        Profile {
471            avatar: self._fields.0,
472            bluesky: self._fields.1.unwrap(),
473            description: self._fields.2,
474            links: self._fields.3,
475            location: self._fields.4,
476            pinned_repositories: self._fields.5,
477            pronouns: self._fields.6,
478            stats: self._fields.7,
479            extra_data: Default::default(),
480        }
481    }
482    /// Build the final struct with custom extra_data
483    pub fn build_with_data(
484        self,
485        extra_data: BTreeMap<
486            jacquard_common::deps::smol_str::SmolStr,
487            jacquard_common::types::value::Data<'a>,
488        >,
489    ) -> Profile<'a> {
490        Profile {
491            avatar: self._fields.0,
492            bluesky: self._fields.1.unwrap(),
493            description: self._fields.2,
494            links: self._fields.3,
495            location: self._fields.4,
496            pinned_repositories: self._fields.5,
497            pronouns: self._fields.6,
498            stats: self._fields.7,
499            extra_data: Some(extra_data),
500        }
501    }
502}
503
504fn lexicon_doc_sh_tangled_actor_profile() -> LexiconDoc<'static> {
505    #[allow(unused_imports)]
506    use jacquard_common::{CowStr, deps::smol_str::SmolStr, types::blob::MimeType};
507    use jacquard_lexicon::lexicon::*;
508    use alloc::collections::BTreeMap;
509    LexiconDoc {
510        lexicon: Lexicon::Lexicon1,
511        id: CowStr::new_static("sh.tangled.actor.profile"),
512        defs: {
513            let mut map = BTreeMap::new();
514            map.insert(
515                SmolStr::new_static("main"),
516                LexUserType::Record(LexRecord {
517                    description: Some(
518                        CowStr::new_static("A declaration of a Tangled account profile."),
519                    ),
520                    key: Some(CowStr::new_static("literal:self")),
521                    record: LexRecordRecord::Object(LexObject {
522                        required: Some(vec![SmolStr::new_static("bluesky")]),
523                        properties: {
524                            #[allow(unused_mut)]
525                            let mut map = BTreeMap::new();
526                            map.insert(
527                                SmolStr::new_static("avatar"),
528                                LexObjectProperty::Blob(LexBlob { ..Default::default() }),
529                            );
530                            map.insert(
531                                SmolStr::new_static("bluesky"),
532                                LexObjectProperty::Boolean(LexBoolean {
533                                    ..Default::default()
534                                }),
535                            );
536                            map.insert(
537                                SmolStr::new_static("description"),
538                                LexObjectProperty::String(LexString {
539                                    description: Some(
540                                        CowStr::new_static("Free-form profile description text."),
541                                    ),
542                                    max_length: Some(2560usize),
543                                    max_graphemes: Some(256usize),
544                                    ..Default::default()
545                                }),
546                            );
547                            map.insert(
548                                SmolStr::new_static("links"),
549                                LexObjectProperty::Array(LexArray {
550                                    items: LexArrayItem::String(LexString {
551                                        description: Some(
552                                            CowStr::new_static(
553                                                "Any URI, intended for social profiles or websites, can be used to link DIDs/AT-URIs too.",
554                                            ),
555                                        ),
556                                        format: Some(LexStringFormat::Uri),
557                                        ..Default::default()
558                                    }),
559                                    min_length: Some(0usize),
560                                    max_length: Some(5usize),
561                                    ..Default::default()
562                                }),
563                            );
564                            map.insert(
565                                SmolStr::new_static("location"),
566                                LexObjectProperty::String(LexString {
567                                    description: Some(
568                                        CowStr::new_static("Free-form location text."),
569                                    ),
570                                    max_length: Some(400usize),
571                                    max_graphemes: Some(40usize),
572                                    ..Default::default()
573                                }),
574                            );
575                            map.insert(
576                                SmolStr::new_static("pinnedRepositories"),
577                                LexObjectProperty::Array(LexArray {
578                                    description: Some(
579                                        CowStr::new_static(
580                                            "Any ATURI, it is up to appviews to validate these fields.",
581                                        ),
582                                    ),
583                                    items: LexArrayItem::String(LexString {
584                                        format: Some(LexStringFormat::AtUri),
585                                        ..Default::default()
586                                    }),
587                                    min_length: Some(0usize),
588                                    max_length: Some(6usize),
589                                    ..Default::default()
590                                }),
591                            );
592                            map.insert(
593                                SmolStr::new_static("pronouns"),
594                                LexObjectProperty::String(LexString {
595                                    description: Some(
596                                        CowStr::new_static("Preferred gender pronouns."),
597                                    ),
598                                    max_length: Some(40usize),
599                                    ..Default::default()
600                                }),
601                            );
602                            map.insert(
603                                SmolStr::new_static("stats"),
604                                LexObjectProperty::Array(LexArray {
605                                    items: LexArrayItem::String(LexString {
606                                        description: Some(CowStr::new_static("Vanity stats.")),
607                                        ..Default::default()
608                                    }),
609                                    min_length: Some(0usize),
610                                    max_length: Some(2usize),
611                                    ..Default::default()
612                                }),
613                            );
614                            map
615                        },
616                        ..Default::default()
617                    }),
618                    ..Default::default()
619                }),
620            );
621            map
622        },
623        ..Default::default()
624    }
625}