1#[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::{Did, 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};
29use crate::app_bsky::richtext::facet::Facet;
30use crate::com_atproto::label::SelfLabels;
31#[lexicon]
34#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
35#[serde(rename_all = "camelCase", rename = "app.bsky.feed.generator", tag = "$type")]
36pub struct Generator<'a> {
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub accepts_interactions: Option<bool>,
40 #[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 #[serde(borrow)]
45 pub content_mode: Option<GeneratorContentMode<'a>>,
46 pub created_at: Datetime,
47 #[serde(skip_serializing_if = "Option::is_none")]
48 #[serde(borrow)]
49 pub description: Option<CowStr<'a>>,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 #[serde(borrow)]
52 pub description_facets: Option<Vec<Facet<'a>>>,
53 #[serde(borrow)]
54 pub did: Did<'a>,
55 #[serde(borrow)]
56 pub display_name: CowStr<'a>,
57 #[serde(skip_serializing_if = "Option::is_none")]
59 #[serde(borrow)]
60 pub labels: Option<SelfLabels<'a>>,
61}
62
63
64#[derive(Debug, Clone, PartialEq, Eq, Hash)]
65pub enum GeneratorContentMode<'a> {
66 ContentModeUnspecified,
67 ContentModeVideo,
68 Other(CowStr<'a>),
69}
70
71impl<'a> GeneratorContentMode<'a> {
72 pub fn as_str(&self) -> &str {
73 match self {
74 Self::ContentModeUnspecified => "app.bsky.feed.defs#contentModeUnspecified",
75 Self::ContentModeVideo => "app.bsky.feed.defs#contentModeVideo",
76 Self::Other(s) => s.as_ref(),
77 }
78 }
79}
80
81impl<'a> From<&'a str> for GeneratorContentMode<'a> {
82 fn from(s: &'a str) -> Self {
83 match s {
84 "app.bsky.feed.defs#contentModeUnspecified" => Self::ContentModeUnspecified,
85 "app.bsky.feed.defs#contentModeVideo" => Self::ContentModeVideo,
86 _ => Self::Other(CowStr::from(s)),
87 }
88 }
89}
90
91impl<'a> From<String> for GeneratorContentMode<'a> {
92 fn from(s: String) -> Self {
93 match s.as_str() {
94 "app.bsky.feed.defs#contentModeUnspecified" => Self::ContentModeUnspecified,
95 "app.bsky.feed.defs#contentModeVideo" => Self::ContentModeVideo,
96 _ => Self::Other(CowStr::from(s)),
97 }
98 }
99}
100
101impl<'a> core::fmt::Display for GeneratorContentMode<'a> {
102 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
103 write!(f, "{}", self.as_str())
104 }
105}
106
107impl<'a> AsRef<str> for GeneratorContentMode<'a> {
108 fn as_ref(&self) -> &str {
109 self.as_str()
110 }
111}
112
113impl<'a> serde::Serialize for GeneratorContentMode<'a> {
114 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
115 where
116 S: serde::Serializer,
117 {
118 serializer.serialize_str(self.as_str())
119 }
120}
121
122impl<'de, 'a> serde::Deserialize<'de> for GeneratorContentMode<'a>
123where
124 'de: 'a,
125{
126 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
127 where
128 D: serde::Deserializer<'de>,
129 {
130 let s = <&'de str>::deserialize(deserializer)?;
131 Ok(Self::from(s))
132 }
133}
134
135impl<'a> Default for GeneratorContentMode<'a> {
136 fn default() -> Self {
137 Self::Other(Default::default())
138 }
139}
140
141impl jacquard_common::IntoStatic for GeneratorContentMode<'_> {
142 type Output = GeneratorContentMode<'static>;
143 fn into_static(self) -> Self::Output {
144 match self {
145 GeneratorContentMode::ContentModeUnspecified => {
146 GeneratorContentMode::ContentModeUnspecified
147 }
148 GeneratorContentMode::ContentModeVideo => {
149 GeneratorContentMode::ContentModeVideo
150 }
151 GeneratorContentMode::Other(v) => {
152 GeneratorContentMode::Other(v.into_static())
153 }
154 }
155 }
156}
157
158#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
161#[serde(rename_all = "camelCase")]
162pub struct GeneratorGetRecordOutput<'a> {
163 #[serde(skip_serializing_if = "Option::is_none")]
164 #[serde(borrow)]
165 pub cid: Option<Cid<'a>>,
166 #[serde(borrow)]
167 pub uri: AtUri<'a>,
168 #[serde(borrow)]
169 pub value: Generator<'a>,
170}
171
172impl<'a> Generator<'a> {
173 pub fn uri(
174 uri: impl Into<CowStr<'a>>,
175 ) -> Result<RecordUri<'a, GeneratorRecord>, UriError> {
176 RecordUri::try_from_uri(AtUri::new_cow(uri.into())?)
177 }
178}
179
180#[derive(Debug, Serialize, Deserialize)]
183pub struct GeneratorRecord;
184impl XrpcResp for GeneratorRecord {
185 const NSID: &'static str = "app.bsky.feed.generator";
186 const ENCODING: &'static str = "application/json";
187 type Output<'de> = GeneratorGetRecordOutput<'de>;
188 type Err<'de> = RecordError<'de>;
189}
190
191impl From<GeneratorGetRecordOutput<'_>> for Generator<'_> {
192 fn from(output: GeneratorGetRecordOutput<'_>) -> Self {
193 use jacquard_common::IntoStatic;
194 output.value.into_static()
195 }
196}
197
198impl Collection for Generator<'_> {
199 const NSID: &'static str = "app.bsky.feed.generator";
200 type Record = GeneratorRecord;
201}
202
203impl Collection for GeneratorRecord {
204 const NSID: &'static str = "app.bsky.feed.generator";
205 type Record = GeneratorRecord;
206}
207
208impl<'a> LexiconSchema for Generator<'a> {
209 fn nsid() -> &'static str {
210 "app.bsky.feed.generator"
211 }
212 fn def_name() -> &'static str {
213 "main"
214 }
215 fn lexicon_doc() -> LexiconDoc<'static> {
216 lexicon_doc_app_bsky_feed_generator()
217 }
218 fn validate(&self) -> Result<(), ConstraintError> {
219 if let Some(ref value) = self.avatar {
220 {
221 let size = value.blob().size;
222 if size > 1000000usize {
223 return Err(ConstraintError::BlobTooLarge {
224 path: ValidationPath::from_field("avatar"),
225 max: 1000000usize,
226 actual: size,
227 });
228 }
229 }
230 }
231 if let Some(ref value) = self.avatar {
232 {
233 let mime = value.blob().mime_type.as_str();
234 let accepted: &[&str] = &["image/png", "image/jpeg"];
235 let matched = accepted
236 .iter()
237 .any(|pattern| {
238 if *pattern == "*/*" {
239 true
240 } else if pattern.ends_with("/*") {
241 let prefix = &pattern[..pattern.len() - 2];
242 mime.starts_with(prefix)
243 && mime.as_bytes().get(prefix.len()) == Some(&b'/')
244 } else {
245 mime == *pattern
246 }
247 });
248 if !matched {
249 return Err(ConstraintError::BlobMimeTypeNotAccepted {
250 path: ValidationPath::from_field("avatar"),
251 accepted: vec![
252 "image/png".to_string(), "image/jpeg".to_string()
253 ],
254 actual: mime.to_string(),
255 });
256 }
257 }
258 }
259 if let Some(ref value) = self.description {
260 #[allow(unused_comparisons)]
261 if <str>::len(value.as_ref()) > 3000usize {
262 return Err(ConstraintError::MaxLength {
263 path: ValidationPath::from_field("description"),
264 max: 3000usize,
265 actual: <str>::len(value.as_ref()),
266 });
267 }
268 }
269 if let Some(ref value) = self.description {
270 {
271 let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
272 if count > 300usize {
273 return Err(ConstraintError::MaxGraphemes {
274 path: ValidationPath::from_field("description"),
275 max: 300usize,
276 actual: count,
277 });
278 }
279 }
280 }
281 {
282 let value = &self.display_name;
283 #[allow(unused_comparisons)]
284 if <str>::len(value.as_ref()) > 240usize {
285 return Err(ConstraintError::MaxLength {
286 path: ValidationPath::from_field("display_name"),
287 max: 240usize,
288 actual: <str>::len(value.as_ref()),
289 });
290 }
291 }
292 {
293 let value = &self.display_name;
294 {
295 let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
296 if count > 24usize {
297 return Err(ConstraintError::MaxGraphemes {
298 path: ValidationPath::from_field("display_name"),
299 max: 24usize,
300 actual: count,
301 });
302 }
303 }
304 }
305 Ok(())
306 }
307}
308
309pub mod generator_state {
310
311 pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
312 #[allow(unused)]
313 use ::core::marker::PhantomData;
314 mod sealed {
315 pub trait Sealed {}
316 }
317 pub trait State: sealed::Sealed {
319 type DisplayName;
320 type CreatedAt;
321 type Did;
322 }
323 pub struct Empty(());
325 impl sealed::Sealed for Empty {}
326 impl State for Empty {
327 type DisplayName = Unset;
328 type CreatedAt = Unset;
329 type Did = Unset;
330 }
331 pub struct SetDisplayName<S: State = Empty>(PhantomData<fn() -> S>);
333 impl<S: State> sealed::Sealed for SetDisplayName<S> {}
334 impl<S: State> State for SetDisplayName<S> {
335 type DisplayName = Set<members::display_name>;
336 type CreatedAt = S::CreatedAt;
337 type Did = S::Did;
338 }
339 pub struct SetCreatedAt<S: State = Empty>(PhantomData<fn() -> S>);
341 impl<S: State> sealed::Sealed for SetCreatedAt<S> {}
342 impl<S: State> State for SetCreatedAt<S> {
343 type DisplayName = S::DisplayName;
344 type CreatedAt = Set<members::created_at>;
345 type Did = S::Did;
346 }
347 pub struct SetDid<S: State = Empty>(PhantomData<fn() -> S>);
349 impl<S: State> sealed::Sealed for SetDid<S> {}
350 impl<S: State> State for SetDid<S> {
351 type DisplayName = S::DisplayName;
352 type CreatedAt = S::CreatedAt;
353 type Did = Set<members::did>;
354 }
355 #[allow(non_camel_case_types)]
357 pub mod members {
358 pub struct display_name(());
360 pub struct created_at(());
362 pub struct did(());
364 }
365}
366
367pub struct GeneratorBuilder<'a, S: generator_state::State> {
369 _state: PhantomData<fn() -> S>,
370 _fields: (
371 Option<bool>,
372 Option<BlobRef<'a>>,
373 Option<GeneratorContentMode<'a>>,
374 Option<Datetime>,
375 Option<CowStr<'a>>,
376 Option<Vec<Facet<'a>>>,
377 Option<Did<'a>>,
378 Option<CowStr<'a>>,
379 Option<SelfLabels<'a>>,
380 ),
381 _lifetime: PhantomData<&'a ()>,
382}
383
384impl<'a> Generator<'a> {
385 pub fn new() -> GeneratorBuilder<'a, generator_state::Empty> {
387 GeneratorBuilder::new()
388 }
389}
390
391impl<'a> GeneratorBuilder<'a, generator_state::Empty> {
392 pub fn new() -> Self {
394 GeneratorBuilder {
395 _state: PhantomData,
396 _fields: (None, None, None, None, None, None, None, None, None),
397 _lifetime: PhantomData,
398 }
399 }
400}
401
402impl<'a, S: generator_state::State> GeneratorBuilder<'a, S> {
403 pub fn accepts_interactions(mut self, value: impl Into<Option<bool>>) -> Self {
405 self._fields.0 = value.into();
406 self
407 }
408 pub fn maybe_accepts_interactions(mut self, value: Option<bool>) -> Self {
410 self._fields.0 = value;
411 self
412 }
413}
414
415impl<'a, S: generator_state::State> GeneratorBuilder<'a, S> {
416 pub fn avatar(mut self, value: impl Into<Option<BlobRef<'a>>>) -> Self {
418 self._fields.1 = value.into();
419 self
420 }
421 pub fn maybe_avatar(mut self, value: Option<BlobRef<'a>>) -> Self {
423 self._fields.1 = value;
424 self
425 }
426}
427
428impl<'a, S: generator_state::State> GeneratorBuilder<'a, S> {
429 pub fn content_mode(
431 mut self,
432 value: impl Into<Option<GeneratorContentMode<'a>>>,
433 ) -> Self {
434 self._fields.2 = value.into();
435 self
436 }
437 pub fn maybe_content_mode(
439 mut self,
440 value: Option<GeneratorContentMode<'a>>,
441 ) -> Self {
442 self._fields.2 = value;
443 self
444 }
445}
446
447impl<'a, S> GeneratorBuilder<'a, S>
448where
449 S: generator_state::State,
450 S::CreatedAt: generator_state::IsUnset,
451{
452 pub fn created_at(
454 mut self,
455 value: impl Into<Datetime>,
456 ) -> GeneratorBuilder<'a, generator_state::SetCreatedAt<S>> {
457 self._fields.3 = Option::Some(value.into());
458 GeneratorBuilder {
459 _state: PhantomData,
460 _fields: self._fields,
461 _lifetime: PhantomData,
462 }
463 }
464}
465
466impl<'a, S: generator_state::State> GeneratorBuilder<'a, S> {
467 pub fn description(mut self, value: impl Into<Option<CowStr<'a>>>) -> Self {
469 self._fields.4 = value.into();
470 self
471 }
472 pub fn maybe_description(mut self, value: Option<CowStr<'a>>) -> Self {
474 self._fields.4 = value;
475 self
476 }
477}
478
479impl<'a, S: generator_state::State> GeneratorBuilder<'a, S> {
480 pub fn description_facets(
482 mut self,
483 value: impl Into<Option<Vec<Facet<'a>>>>,
484 ) -> Self {
485 self._fields.5 = value.into();
486 self
487 }
488 pub fn maybe_description_facets(mut self, value: Option<Vec<Facet<'a>>>) -> Self {
490 self._fields.5 = value;
491 self
492 }
493}
494
495impl<'a, S> GeneratorBuilder<'a, S>
496where
497 S: generator_state::State,
498 S::Did: generator_state::IsUnset,
499{
500 pub fn did(
502 mut self,
503 value: impl Into<Did<'a>>,
504 ) -> GeneratorBuilder<'a, generator_state::SetDid<S>> {
505 self._fields.6 = Option::Some(value.into());
506 GeneratorBuilder {
507 _state: PhantomData,
508 _fields: self._fields,
509 _lifetime: PhantomData,
510 }
511 }
512}
513
514impl<'a, S> GeneratorBuilder<'a, S>
515where
516 S: generator_state::State,
517 S::DisplayName: generator_state::IsUnset,
518{
519 pub fn display_name(
521 mut self,
522 value: impl Into<CowStr<'a>>,
523 ) -> GeneratorBuilder<'a, generator_state::SetDisplayName<S>> {
524 self._fields.7 = Option::Some(value.into());
525 GeneratorBuilder {
526 _state: PhantomData,
527 _fields: self._fields,
528 _lifetime: PhantomData,
529 }
530 }
531}
532
533impl<'a, S: generator_state::State> GeneratorBuilder<'a, S> {
534 pub fn labels(mut self, value: impl Into<Option<SelfLabels<'a>>>) -> Self {
536 self._fields.8 = value.into();
537 self
538 }
539 pub fn maybe_labels(mut self, value: Option<SelfLabels<'a>>) -> Self {
541 self._fields.8 = value;
542 self
543 }
544}
545
546impl<'a, S> GeneratorBuilder<'a, S>
547where
548 S: generator_state::State,
549 S::DisplayName: generator_state::IsSet,
550 S::CreatedAt: generator_state::IsSet,
551 S::Did: generator_state::IsSet,
552{
553 pub fn build(self) -> Generator<'a> {
555 Generator {
556 accepts_interactions: self._fields.0,
557 avatar: self._fields.1,
558 content_mode: self._fields.2,
559 created_at: self._fields.3.unwrap(),
560 description: self._fields.4,
561 description_facets: self._fields.5,
562 did: self._fields.6.unwrap(),
563 display_name: self._fields.7.unwrap(),
564 labels: self._fields.8,
565 extra_data: Default::default(),
566 }
567 }
568 pub fn build_with_data(
570 self,
571 extra_data: BTreeMap<
572 jacquard_common::deps::smol_str::SmolStr,
573 jacquard_common::types::value::Data<'a>,
574 >,
575 ) -> Generator<'a> {
576 Generator {
577 accepts_interactions: self._fields.0,
578 avatar: self._fields.1,
579 content_mode: self._fields.2,
580 created_at: self._fields.3.unwrap(),
581 description: self._fields.4,
582 description_facets: self._fields.5,
583 did: self._fields.6.unwrap(),
584 display_name: self._fields.7.unwrap(),
585 labels: self._fields.8,
586 extra_data: Some(extra_data),
587 }
588 }
589}
590
591fn lexicon_doc_app_bsky_feed_generator() -> LexiconDoc<'static> {
592 #[allow(unused_imports)]
593 use jacquard_common::{CowStr, deps::smol_str::SmolStr, types::blob::MimeType};
594 use jacquard_lexicon::lexicon::*;
595 use alloc::collections::BTreeMap;
596 LexiconDoc {
597 lexicon: Lexicon::Lexicon1,
598 id: CowStr::new_static("app.bsky.feed.generator"),
599 defs: {
600 let mut map = BTreeMap::new();
601 map.insert(
602 SmolStr::new_static("main"),
603 LexUserType::Record(LexRecord {
604 description: Some(
605 CowStr::new_static(
606 "Record declaring of the existence of a feed generator, and containing metadata about it. The record can exist in any repository.",
607 ),
608 ),
609 key: Some(CowStr::new_static("any")),
610 record: LexRecordRecord::Object(LexObject {
611 required: Some(
612 vec![
613 SmolStr::new_static("did"),
614 SmolStr::new_static("displayName"),
615 SmolStr::new_static("createdAt")
616 ],
617 ),
618 properties: {
619 #[allow(unused_mut)]
620 let mut map = BTreeMap::new();
621 map.insert(
622 SmolStr::new_static("acceptsInteractions"),
623 LexObjectProperty::Boolean(LexBoolean {
624 ..Default::default()
625 }),
626 );
627 map.insert(
628 SmolStr::new_static("avatar"),
629 LexObjectProperty::Blob(LexBlob { ..Default::default() }),
630 );
631 map.insert(
632 SmolStr::new_static("contentMode"),
633 LexObjectProperty::String(LexString {
634 ..Default::default()
635 }),
636 );
637 map.insert(
638 SmolStr::new_static("createdAt"),
639 LexObjectProperty::String(LexString {
640 format: Some(LexStringFormat::Datetime),
641 ..Default::default()
642 }),
643 );
644 map.insert(
645 SmolStr::new_static("description"),
646 LexObjectProperty::String(LexString {
647 max_length: Some(3000usize),
648 max_graphemes: Some(300usize),
649 ..Default::default()
650 }),
651 );
652 map.insert(
653 SmolStr::new_static("descriptionFacets"),
654 LexObjectProperty::Array(LexArray {
655 items: LexArrayItem::Ref(LexRef {
656 r#ref: CowStr::new_static("app.bsky.richtext.facet"),
657 ..Default::default()
658 }),
659 ..Default::default()
660 }),
661 );
662 map.insert(
663 SmolStr::new_static("did"),
664 LexObjectProperty::String(LexString {
665 format: Some(LexStringFormat::Did),
666 ..Default::default()
667 }),
668 );
669 map.insert(
670 SmolStr::new_static("displayName"),
671 LexObjectProperty::String(LexString {
672 max_length: Some(240usize),
673 max_graphemes: Some(24usize),
674 ..Default::default()
675 }),
676 );
677 map.insert(
678 SmolStr::new_static("labels"),
679 LexObjectProperty::Union(LexRefUnion {
680 description: Some(CowStr::new_static("Self-label values")),
681 refs: vec![
682 CowStr::new_static("com.atproto.label.defs#selfLabels")
683 ],
684 ..Default::default()
685 }),
686 );
687 map
688 },
689 ..Default::default()
690 }),
691 ..Default::default()
692 }),
693 );
694 map
695 },
696 ..Default::default()
697 }
698}