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::{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::graph::ListPurpose;
30use crate::app_bsky::richtext::facet::Facet;
31use crate::com_atproto::label::SelfLabels;
32#[lexicon]
35#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
36#[serde(rename_all = "camelCase", rename = "app.bsky.graph.list", tag = "$type")]
37pub struct List<'a> {
38 #[serde(skip_serializing_if = "Option::is_none")]
39 #[serde(borrow)]
40 pub avatar: Option<BlobRef<'a>>,
41 pub created_at: Datetime,
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 description_facets: Option<Vec<Facet<'a>>>,
48 #[serde(skip_serializing_if = "Option::is_none")]
49 #[serde(borrow)]
50 pub labels: Option<SelfLabels<'a>>,
51 #[serde(borrow)]
53 pub name: CowStr<'a>,
54 #[serde(borrow)]
56 pub purpose: ListPurpose<'a>,
57}
58
59#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
62#[serde(rename_all = "camelCase")]
63pub struct ListGetRecordOutput<'a> {
64 #[serde(skip_serializing_if = "Option::is_none")]
65 #[serde(borrow)]
66 pub cid: Option<Cid<'a>>,
67 #[serde(borrow)]
68 pub uri: AtUri<'a>,
69 #[serde(borrow)]
70 pub value: List<'a>,
71}
72
73impl<'a> List<'a> {
74 pub fn uri(
75 uri: impl Into<CowStr<'a>>,
76 ) -> Result<RecordUri<'a, ListRecord>, UriError> {
77 RecordUri::try_from_uri(AtUri::new_cow(uri.into())?)
78 }
79}
80
81#[derive(Debug, Serialize, Deserialize)]
84pub struct ListRecord;
85impl XrpcResp for ListRecord {
86 const NSID: &'static str = "app.bsky.graph.list";
87 const ENCODING: &'static str = "application/json";
88 type Output<'de> = ListGetRecordOutput<'de>;
89 type Err<'de> = RecordError<'de>;
90}
91
92impl From<ListGetRecordOutput<'_>> for List<'_> {
93 fn from(output: ListGetRecordOutput<'_>) -> Self {
94 use jacquard_common::IntoStatic;
95 output.value.into_static()
96 }
97}
98
99impl Collection for List<'_> {
100 const NSID: &'static str = "app.bsky.graph.list";
101 type Record = ListRecord;
102}
103
104impl Collection for ListRecord {
105 const NSID: &'static str = "app.bsky.graph.list";
106 type Record = ListRecord;
107}
108
109impl<'a> LexiconSchema for List<'a> {
110 fn nsid() -> &'static str {
111 "app.bsky.graph.list"
112 }
113 fn def_name() -> &'static str {
114 "main"
115 }
116 fn lexicon_doc() -> LexiconDoc<'static> {
117 lexicon_doc_app_bsky_graph_list()
118 }
119 fn validate(&self) -> Result<(), ConstraintError> {
120 if let Some(ref value) = self.avatar {
121 {
122 let size = value.blob().size;
123 if size > 1000000usize {
124 return Err(ConstraintError::BlobTooLarge {
125 path: ValidationPath::from_field("avatar"),
126 max: 1000000usize,
127 actual: size,
128 });
129 }
130 }
131 }
132 if let Some(ref value) = self.avatar {
133 {
134 let mime = value.blob().mime_type.as_str();
135 let accepted: &[&str] = &["image/png", "image/jpeg"];
136 let matched = accepted
137 .iter()
138 .any(|pattern| {
139 if *pattern == "*/*" {
140 true
141 } else if pattern.ends_with("/*") {
142 let prefix = &pattern[..pattern.len() - 2];
143 mime.starts_with(prefix)
144 && mime.as_bytes().get(prefix.len()) == Some(&b'/')
145 } else {
146 mime == *pattern
147 }
148 });
149 if !matched {
150 return Err(ConstraintError::BlobMimeTypeNotAccepted {
151 path: ValidationPath::from_field("avatar"),
152 accepted: vec![
153 "image/png".to_string(), "image/jpeg".to_string()
154 ],
155 actual: mime.to_string(),
156 });
157 }
158 }
159 }
160 if let Some(ref value) = self.description {
161 #[allow(unused_comparisons)]
162 if <str>::len(value.as_ref()) > 3000usize {
163 return Err(ConstraintError::MaxLength {
164 path: ValidationPath::from_field("description"),
165 max: 3000usize,
166 actual: <str>::len(value.as_ref()),
167 });
168 }
169 }
170 if let Some(ref value) = self.description {
171 {
172 let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
173 if count > 300usize {
174 return Err(ConstraintError::MaxGraphemes {
175 path: ValidationPath::from_field("description"),
176 max: 300usize,
177 actual: count,
178 });
179 }
180 }
181 }
182 {
183 let value = &self.name;
184 #[allow(unused_comparisons)]
185 if <str>::len(value.as_ref()) > 64usize {
186 return Err(ConstraintError::MaxLength {
187 path: ValidationPath::from_field("name"),
188 max: 64usize,
189 actual: <str>::len(value.as_ref()),
190 });
191 }
192 }
193 {
194 let value = &self.name;
195 #[allow(unused_comparisons)]
196 if <str>::len(value.as_ref()) < 1usize {
197 return Err(ConstraintError::MinLength {
198 path: ValidationPath::from_field("name"),
199 min: 1usize,
200 actual: <str>::len(value.as_ref()),
201 });
202 }
203 }
204 Ok(())
205 }
206}
207
208pub mod list_state {
209
210 pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
211 #[allow(unused)]
212 use ::core::marker::PhantomData;
213 mod sealed {
214 pub trait Sealed {}
215 }
216 pub trait State: sealed::Sealed {
218 type CreatedAt;
219 type Name;
220 type Purpose;
221 }
222 pub struct Empty(());
224 impl sealed::Sealed for Empty {}
225 impl State for Empty {
226 type CreatedAt = Unset;
227 type Name = Unset;
228 type Purpose = Unset;
229 }
230 pub struct SetCreatedAt<S: State = Empty>(PhantomData<fn() -> S>);
232 impl<S: State> sealed::Sealed for SetCreatedAt<S> {}
233 impl<S: State> State for SetCreatedAt<S> {
234 type CreatedAt = Set<members::created_at>;
235 type Name = S::Name;
236 type Purpose = S::Purpose;
237 }
238 pub struct SetName<S: State = Empty>(PhantomData<fn() -> S>);
240 impl<S: State> sealed::Sealed for SetName<S> {}
241 impl<S: State> State for SetName<S> {
242 type CreatedAt = S::CreatedAt;
243 type Name = Set<members::name>;
244 type Purpose = S::Purpose;
245 }
246 pub struct SetPurpose<S: State = Empty>(PhantomData<fn() -> S>);
248 impl<S: State> sealed::Sealed for SetPurpose<S> {}
249 impl<S: State> State for SetPurpose<S> {
250 type CreatedAt = S::CreatedAt;
251 type Name = S::Name;
252 type Purpose = Set<members::purpose>;
253 }
254 #[allow(non_camel_case_types)]
256 pub mod members {
257 pub struct created_at(());
259 pub struct name(());
261 pub struct purpose(());
263 }
264}
265
266pub struct ListBuilder<'a, S: list_state::State> {
268 _state: PhantomData<fn() -> S>,
269 _fields: (
270 Option<BlobRef<'a>>,
271 Option<Datetime>,
272 Option<CowStr<'a>>,
273 Option<Vec<Facet<'a>>>,
274 Option<SelfLabels<'a>>,
275 Option<CowStr<'a>>,
276 Option<ListPurpose<'a>>,
277 ),
278 _lifetime: PhantomData<&'a ()>,
279}
280
281impl<'a> List<'a> {
282 pub fn new() -> ListBuilder<'a, list_state::Empty> {
284 ListBuilder::new()
285 }
286}
287
288impl<'a> ListBuilder<'a, list_state::Empty> {
289 pub fn new() -> Self {
291 ListBuilder {
292 _state: PhantomData,
293 _fields: (None, None, None, None, None, None, None),
294 _lifetime: PhantomData,
295 }
296 }
297}
298
299impl<'a, S: list_state::State> ListBuilder<'a, S> {
300 pub fn avatar(mut self, value: impl Into<Option<BlobRef<'a>>>) -> Self {
302 self._fields.0 = value.into();
303 self
304 }
305 pub fn maybe_avatar(mut self, value: Option<BlobRef<'a>>) -> Self {
307 self._fields.0 = value;
308 self
309 }
310}
311
312impl<'a, S> ListBuilder<'a, S>
313where
314 S: list_state::State,
315 S::CreatedAt: list_state::IsUnset,
316{
317 pub fn created_at(
319 mut self,
320 value: impl Into<Datetime>,
321 ) -> ListBuilder<'a, list_state::SetCreatedAt<S>> {
322 self._fields.1 = Option::Some(value.into());
323 ListBuilder {
324 _state: PhantomData,
325 _fields: self._fields,
326 _lifetime: PhantomData,
327 }
328 }
329}
330
331impl<'a, S: list_state::State> ListBuilder<'a, S> {
332 pub fn description(mut self, value: impl Into<Option<CowStr<'a>>>) -> Self {
334 self._fields.2 = value.into();
335 self
336 }
337 pub fn maybe_description(mut self, value: Option<CowStr<'a>>) -> Self {
339 self._fields.2 = value;
340 self
341 }
342}
343
344impl<'a, S: list_state::State> ListBuilder<'a, S> {
345 pub fn description_facets(
347 mut self,
348 value: impl Into<Option<Vec<Facet<'a>>>>,
349 ) -> Self {
350 self._fields.3 = value.into();
351 self
352 }
353 pub fn maybe_description_facets(mut self, value: Option<Vec<Facet<'a>>>) -> Self {
355 self._fields.3 = value;
356 self
357 }
358}
359
360impl<'a, S: list_state::State> ListBuilder<'a, S> {
361 pub fn labels(mut self, value: impl Into<Option<SelfLabels<'a>>>) -> Self {
363 self._fields.4 = value.into();
364 self
365 }
366 pub fn maybe_labels(mut self, value: Option<SelfLabels<'a>>) -> Self {
368 self._fields.4 = value;
369 self
370 }
371}
372
373impl<'a, S> ListBuilder<'a, S>
374where
375 S: list_state::State,
376 S::Name: list_state::IsUnset,
377{
378 pub fn name(
380 mut self,
381 value: impl Into<CowStr<'a>>,
382 ) -> ListBuilder<'a, list_state::SetName<S>> {
383 self._fields.5 = Option::Some(value.into());
384 ListBuilder {
385 _state: PhantomData,
386 _fields: self._fields,
387 _lifetime: PhantomData,
388 }
389 }
390}
391
392impl<'a, S> ListBuilder<'a, S>
393where
394 S: list_state::State,
395 S::Purpose: list_state::IsUnset,
396{
397 pub fn purpose(
399 mut self,
400 value: impl Into<ListPurpose<'a>>,
401 ) -> ListBuilder<'a, list_state::SetPurpose<S>> {
402 self._fields.6 = Option::Some(value.into());
403 ListBuilder {
404 _state: PhantomData,
405 _fields: self._fields,
406 _lifetime: PhantomData,
407 }
408 }
409}
410
411impl<'a, S> ListBuilder<'a, S>
412where
413 S: list_state::State,
414 S::CreatedAt: list_state::IsSet,
415 S::Name: list_state::IsSet,
416 S::Purpose: list_state::IsSet,
417{
418 pub fn build(self) -> List<'a> {
420 List {
421 avatar: self._fields.0,
422 created_at: self._fields.1.unwrap(),
423 description: self._fields.2,
424 description_facets: self._fields.3,
425 labels: self._fields.4,
426 name: self._fields.5.unwrap(),
427 purpose: self._fields.6.unwrap(),
428 extra_data: Default::default(),
429 }
430 }
431 pub fn build_with_data(
433 self,
434 extra_data: BTreeMap<
435 jacquard_common::deps::smol_str::SmolStr,
436 jacquard_common::types::value::Data<'a>,
437 >,
438 ) -> List<'a> {
439 List {
440 avatar: self._fields.0,
441 created_at: self._fields.1.unwrap(),
442 description: self._fields.2,
443 description_facets: self._fields.3,
444 labels: self._fields.4,
445 name: self._fields.5.unwrap(),
446 purpose: self._fields.6.unwrap(),
447 extra_data: Some(extra_data),
448 }
449 }
450}
451
452fn lexicon_doc_app_bsky_graph_list() -> LexiconDoc<'static> {
453 #[allow(unused_imports)]
454 use jacquard_common::{CowStr, deps::smol_str::SmolStr, types::blob::MimeType};
455 use jacquard_lexicon::lexicon::*;
456 use alloc::collections::BTreeMap;
457 LexiconDoc {
458 lexicon: Lexicon::Lexicon1,
459 id: CowStr::new_static("app.bsky.graph.list"),
460 defs: {
461 let mut map = BTreeMap::new();
462 map.insert(
463 SmolStr::new_static("main"),
464 LexUserType::Record(LexRecord {
465 description: Some(
466 CowStr::new_static(
467 "Record representing a list of accounts (actors). Scope includes both moderation-oriented lists and curration-oriented lists.",
468 ),
469 ),
470 key: Some(CowStr::new_static("tid")),
471 record: LexRecordRecord::Object(LexObject {
472 required: Some(
473 vec![
474 SmolStr::new_static("name"), SmolStr::new_static("purpose"),
475 SmolStr::new_static("createdAt")
476 ],
477 ),
478 properties: {
479 #[allow(unused_mut)]
480 let mut map = BTreeMap::new();
481 map.insert(
482 SmolStr::new_static("avatar"),
483 LexObjectProperty::Blob(LexBlob { ..Default::default() }),
484 );
485 map.insert(
486 SmolStr::new_static("createdAt"),
487 LexObjectProperty::String(LexString {
488 format: Some(LexStringFormat::Datetime),
489 ..Default::default()
490 }),
491 );
492 map.insert(
493 SmolStr::new_static("description"),
494 LexObjectProperty::String(LexString {
495 max_length: Some(3000usize),
496 max_graphemes: Some(300usize),
497 ..Default::default()
498 }),
499 );
500 map.insert(
501 SmolStr::new_static("descriptionFacets"),
502 LexObjectProperty::Array(LexArray {
503 items: LexArrayItem::Ref(LexRef {
504 r#ref: CowStr::new_static("app.bsky.richtext.facet"),
505 ..Default::default()
506 }),
507 ..Default::default()
508 }),
509 );
510 map.insert(
511 SmolStr::new_static("labels"),
512 LexObjectProperty::Union(LexRefUnion {
513 refs: vec![
514 CowStr::new_static("com.atproto.label.defs#selfLabels")
515 ],
516 ..Default::default()
517 }),
518 );
519 map.insert(
520 SmolStr::new_static("name"),
521 LexObjectProperty::String(LexString {
522 description: Some(
523 CowStr::new_static(
524 "Display name for list; can not be empty.",
525 ),
526 ),
527 min_length: Some(1usize),
528 max_length: Some(64usize),
529 ..Default::default()
530 }),
531 );
532 map.insert(
533 SmolStr::new_static("purpose"),
534 LexObjectProperty::Ref(LexRef {
535 r#ref: CowStr::new_static(
536 "app.bsky.graph.defs#listPurpose",
537 ),
538 ..Default::default()
539 }),
540 );
541 map
542 },
543 ..Default::default()
544 }),
545 ..Default::default()
546 }),
547 );
548 map
549 },
550 ..Default::default()
551 }
552}