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