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};
29#[lexicon]
32#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
33#[serde(rename_all = "camelCase", rename = "net.anisota.feed.list", tag = "$type")]
34pub struct List<'a> {
35 #[serde(skip_serializing_if = "Option::is_none")]
37 #[serde(borrow)]
38 pub avatar: Option<BlobRef<'a>>,
39 pub created_at: Datetime,
41 #[serde(skip_serializing_if = "Option::is_none")]
43 #[serde(borrow)]
44 pub description: Option<CowStr<'a>>,
45 #[serde(borrow)]
47 pub name: CowStr<'a>,
48 #[serde(skip_serializing_if = "Option::is_none")]
50 #[serde(borrow)]
51 pub tags: Option<Vec<CowStr<'a>>>,
52}
53
54#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
57#[serde(rename_all = "camelCase")]
58pub struct ListGetRecordOutput<'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: List<'a>,
66}
67
68impl<'a> List<'a> {
69 pub fn uri(
70 uri: impl Into<CowStr<'a>>,
71 ) -> Result<RecordUri<'a, ListRecord>, UriError> {
72 RecordUri::try_from_uri(AtUri::new_cow(uri.into())?)
73 }
74}
75
76#[derive(Debug, Serialize, Deserialize)]
79pub struct ListRecord;
80impl XrpcResp for ListRecord {
81 const NSID: &'static str = "net.anisota.feed.list";
82 const ENCODING: &'static str = "application/json";
83 type Output<'de> = ListGetRecordOutput<'de>;
84 type Err<'de> = RecordError<'de>;
85}
86
87impl From<ListGetRecordOutput<'_>> for List<'_> {
88 fn from(output: ListGetRecordOutput<'_>) -> Self {
89 use jacquard_common::IntoStatic;
90 output.value.into_static()
91 }
92}
93
94impl Collection for List<'_> {
95 const NSID: &'static str = "net.anisota.feed.list";
96 type Record = ListRecord;
97}
98
99impl Collection for ListRecord {
100 const NSID: &'static str = "net.anisota.feed.list";
101 type Record = ListRecord;
102}
103
104impl<'a> LexiconSchema for List<'a> {
105 fn nsid() -> &'static str {
106 "net.anisota.feed.list"
107 }
108 fn def_name() -> &'static str {
109 "main"
110 }
111 fn lexicon_doc() -> LexiconDoc<'static> {
112 lexicon_doc_net_anisota_feed_list()
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()) > 300usize {
158 return Err(ConstraintError::MaxLength {
159 path: ValidationPath::from_field("description"),
160 max: 300usize,
161 actual: <str>::len(value.as_ref()),
162 });
163 }
164 }
165 {
166 let value = &self.name;
167 #[allow(unused_comparisons)]
168 if <str>::len(value.as_ref()) > 64usize {
169 return Err(ConstraintError::MaxLength {
170 path: ValidationPath::from_field("name"),
171 max: 64usize,
172 actual: <str>::len(value.as_ref()),
173 });
174 }
175 }
176 if let Some(ref value) = self.tags {
177 #[allow(unused_comparisons)]
178 if value.len() > 10usize {
179 return Err(ConstraintError::MaxLength {
180 path: ValidationPath::from_field("tags"),
181 max: 10usize,
182 actual: value.len(),
183 });
184 }
185 }
186 Ok(())
187 }
188}
189
190pub mod list_state {
191
192 pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
193 #[allow(unused)]
194 use ::core::marker::PhantomData;
195 mod sealed {
196 pub trait Sealed {}
197 }
198 pub trait State: sealed::Sealed {
200 type CreatedAt;
201 type Name;
202 }
203 pub struct Empty(());
205 impl sealed::Sealed for Empty {}
206 impl State for Empty {
207 type CreatedAt = Unset;
208 type Name = Unset;
209 }
210 pub struct SetCreatedAt<S: State = Empty>(PhantomData<fn() -> S>);
212 impl<S: State> sealed::Sealed for SetCreatedAt<S> {}
213 impl<S: State> State for SetCreatedAt<S> {
214 type CreatedAt = Set<members::created_at>;
215 type Name = S::Name;
216 }
217 pub struct SetName<S: State = Empty>(PhantomData<fn() -> S>);
219 impl<S: State> sealed::Sealed for SetName<S> {}
220 impl<S: State> State for SetName<S> {
221 type CreatedAt = S::CreatedAt;
222 type Name = Set<members::name>;
223 }
224 #[allow(non_camel_case_types)]
226 pub mod members {
227 pub struct created_at(());
229 pub struct name(());
231 }
232}
233
234pub struct ListBuilder<'a, S: list_state::State> {
236 _state: PhantomData<fn() -> S>,
237 _fields: (
238 Option<BlobRef<'a>>,
239 Option<Datetime>,
240 Option<CowStr<'a>>,
241 Option<CowStr<'a>>,
242 Option<Vec<CowStr<'a>>>,
243 ),
244 _lifetime: PhantomData<&'a ()>,
245}
246
247impl<'a> List<'a> {
248 pub fn new() -> ListBuilder<'a, list_state::Empty> {
250 ListBuilder::new()
251 }
252}
253
254impl<'a> ListBuilder<'a, list_state::Empty> {
255 pub fn new() -> Self {
257 ListBuilder {
258 _state: PhantomData,
259 _fields: (None, None, None, None, None),
260 _lifetime: PhantomData,
261 }
262 }
263}
264
265impl<'a, S: list_state::State> ListBuilder<'a, S> {
266 pub fn avatar(mut self, value: impl Into<Option<BlobRef<'a>>>) -> Self {
268 self._fields.0 = value.into();
269 self
270 }
271 pub fn maybe_avatar(mut self, value: Option<BlobRef<'a>>) -> Self {
273 self._fields.0 = value;
274 self
275 }
276}
277
278impl<'a, S> ListBuilder<'a, S>
279where
280 S: list_state::State,
281 S::CreatedAt: list_state::IsUnset,
282{
283 pub fn created_at(
285 mut self,
286 value: impl Into<Datetime>,
287 ) -> ListBuilder<'a, list_state::SetCreatedAt<S>> {
288 self._fields.1 = Option::Some(value.into());
289 ListBuilder {
290 _state: PhantomData,
291 _fields: self._fields,
292 _lifetime: PhantomData,
293 }
294 }
295}
296
297impl<'a, S: list_state::State> ListBuilder<'a, S> {
298 pub fn description(mut self, value: impl Into<Option<CowStr<'a>>>) -> Self {
300 self._fields.2 = value.into();
301 self
302 }
303 pub fn maybe_description(mut self, value: Option<CowStr<'a>>) -> Self {
305 self._fields.2 = value;
306 self
307 }
308}
309
310impl<'a, S> ListBuilder<'a, S>
311where
312 S: list_state::State,
313 S::Name: list_state::IsUnset,
314{
315 pub fn name(
317 mut self,
318 value: impl Into<CowStr<'a>>,
319 ) -> ListBuilder<'a, list_state::SetName<S>> {
320 self._fields.3 = Option::Some(value.into());
321 ListBuilder {
322 _state: PhantomData,
323 _fields: self._fields,
324 _lifetime: PhantomData,
325 }
326 }
327}
328
329impl<'a, S: list_state::State> ListBuilder<'a, S> {
330 pub fn tags(mut self, value: impl Into<Option<Vec<CowStr<'a>>>>) -> Self {
332 self._fields.4 = value.into();
333 self
334 }
335 pub fn maybe_tags(mut self, value: Option<Vec<CowStr<'a>>>) -> Self {
337 self._fields.4 = value;
338 self
339 }
340}
341
342impl<'a, S> ListBuilder<'a, S>
343where
344 S: list_state::State,
345 S::CreatedAt: list_state::IsSet,
346 S::Name: list_state::IsSet,
347{
348 pub fn build(self) -> List<'a> {
350 List {
351 avatar: self._fields.0,
352 created_at: self._fields.1.unwrap(),
353 description: self._fields.2,
354 name: self._fields.3.unwrap(),
355 tags: self._fields.4,
356 extra_data: Default::default(),
357 }
358 }
359 pub fn build_with_data(
361 self,
362 extra_data: BTreeMap<
363 jacquard_common::deps::smol_str::SmolStr,
364 jacquard_common::types::value::Data<'a>,
365 >,
366 ) -> List<'a> {
367 List {
368 avatar: self._fields.0,
369 created_at: self._fields.1.unwrap(),
370 description: self._fields.2,
371 name: self._fields.3.unwrap(),
372 tags: self._fields.4,
373 extra_data: Some(extra_data),
374 }
375 }
376}
377
378fn lexicon_doc_net_anisota_feed_list() -> LexiconDoc<'static> {
379 #[allow(unused_imports)]
380 use jacquard_common::{CowStr, deps::smol_str::SmolStr, types::blob::MimeType};
381 use jacquard_lexicon::lexicon::*;
382 use alloc::collections::BTreeMap;
383 LexiconDoc {
384 lexicon: Lexicon::Lexicon1,
385 id: CowStr::new_static("net.anisota.feed.list"),
386 defs: {
387 let mut map = BTreeMap::new();
388 map.insert(
389 SmolStr::new_static("main"),
390 LexUserType::Record(LexRecord {
391 description: Some(
392 CowStr::new_static(
393 "A list of posts for curation, bookmarking, or organization",
394 ),
395 ),
396 key: Some(CowStr::new_static("tid")),
397 record: LexRecordRecord::Object(LexObject {
398 required: Some(
399 vec![
400 SmolStr::new_static("name"),
401 SmolStr::new_static("createdAt")
402 ],
403 ),
404 properties: {
405 #[allow(unused_mut)]
406 let mut map = BTreeMap::new();
407 map.insert(
408 SmolStr::new_static("avatar"),
409 LexObjectProperty::Blob(LexBlob { ..Default::default() }),
410 );
411 map.insert(
412 SmolStr::new_static("createdAt"),
413 LexObjectProperty::String(LexString {
414 description: Some(
415 CowStr::new_static("When the list was created"),
416 ),
417 format: Some(LexStringFormat::Datetime),
418 ..Default::default()
419 }),
420 );
421 map.insert(
422 SmolStr::new_static("description"),
423 LexObjectProperty::String(LexString {
424 description: Some(
425 CowStr::new_static("Optional description of the list"),
426 ),
427 max_length: Some(300usize),
428 ..Default::default()
429 }),
430 );
431 map.insert(
432 SmolStr::new_static("name"),
433 LexObjectProperty::String(LexString {
434 description: Some(
435 CowStr::new_static("Display name for the list"),
436 ),
437 max_length: Some(64usize),
438 ..Default::default()
439 }),
440 );
441 map.insert(
442 SmolStr::new_static("tags"),
443 LexObjectProperty::Array(LexArray {
444 description: Some(
445 CowStr::new_static("Tags for categorizing the list"),
446 ),
447 items: LexArrayItem::String(LexString {
448 max_length: Some(32usize),
449 ..Default::default()
450 }),
451 max_length: Some(10usize),
452 ..Default::default()
453 }),
454 );
455 map
456 },
457 ..Default::default()
458 }),
459 ..Default::default()
460 }),
461 );
462 map
463 },
464 ..Default::default()
465 }
466}