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, UriValue};
20use jacquard_common::types::uri::{RecordUri, UriError};
21use jacquard_common::types::value::Data;
22use jacquard_common::xrpc::XrpcResp;
23use jacquard_derive::{IntoStatic, lexicon};
24use jacquard_lexicon::lexicon::LexiconDoc;
25use jacquard_lexicon::schema::LexiconSchema;
26
27#[allow(unused_imports)]
28use jacquard_lexicon::validation::{ConstraintError, ValidationPath};
29use serde::{Serialize, Deserialize};
30use crate::com_atproto::repo::strong_ref::StrongRef;
31
32#[lexicon]
33#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
34#[serde(rename_all = "camelCase", rename = "blog.pckt.post", tag = "$type")]
35pub struct Post<'a> {
36 #[serde(borrow)]
37 pub blocks: Data<'a>,
38 #[serde(borrow)]
39 pub blog: StrongRef<'a>,
40 #[serde(skip_serializing_if = "Option::is_none")]
41 #[serde(borrow)]
42 pub body_plain: Option<CowStr<'a>>,
43 #[serde(skip_serializing_if = "Option::is_none")]
44 #[serde(borrow)]
45 pub cover: Option<BlobRef<'a>>,
46 #[serde(skip_serializing_if = "Option::is_none")]
47 #[serde(borrow)]
48 pub images: Option<Vec<BlobRef<'a>>>,
49 #[serde(skip_serializing_if = "Option::is_none")]
50 pub published_at: Option<Datetime>,
51 #[serde(skip_serializing_if = "Option::is_none")]
52 #[serde(borrow)]
53 pub tags: Option<Vec<CowStr<'a>>>,
54 #[serde(borrow)]
55 pub title: CowStr<'a>,
56 #[serde(skip_serializing_if = "Option::is_none")]
57 pub updated_at: Option<Datetime>,
58 #[serde(borrow)]
59 pub url: UriValue<'a>,
60}
61
62#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
65#[serde(rename_all = "camelCase")]
66pub struct PostGetRecordOutput<'a> {
67 #[serde(skip_serializing_if = "Option::is_none")]
68 #[serde(borrow)]
69 pub cid: Option<Cid<'a>>,
70 #[serde(borrow)]
71 pub uri: AtUri<'a>,
72 #[serde(borrow)]
73 pub value: Post<'a>,
74}
75
76impl<'a> Post<'a> {
77 pub fn uri(
78 uri: impl Into<CowStr<'a>>,
79 ) -> Result<RecordUri<'a, PostRecord>, UriError> {
80 RecordUri::try_from_uri(AtUri::new_cow(uri.into())?)
81 }
82}
83
84#[derive(Debug, Serialize, Deserialize)]
87pub struct PostRecord;
88impl XrpcResp for PostRecord {
89 const NSID: &'static str = "blog.pckt.post";
90 const ENCODING: &'static str = "application/json";
91 type Output<'de> = PostGetRecordOutput<'de>;
92 type Err<'de> = RecordError<'de>;
93}
94
95impl From<PostGetRecordOutput<'_>> for Post<'_> {
96 fn from(output: PostGetRecordOutput<'_>) -> Self {
97 use jacquard_common::IntoStatic;
98 output.value.into_static()
99 }
100}
101
102impl Collection for Post<'_> {
103 const NSID: &'static str = "blog.pckt.post";
104 type Record = PostRecord;
105}
106
107impl Collection for PostRecord {
108 const NSID: &'static str = "blog.pckt.post";
109 type Record = PostRecord;
110}
111
112impl<'a> LexiconSchema for Post<'a> {
113 fn nsid() -> &'static str {
114 "blog.pckt.post"
115 }
116 fn def_name() -> &'static str {
117 "main"
118 }
119 fn lexicon_doc() -> LexiconDoc<'static> {
120 lexicon_doc_blog_pckt_post()
121 }
122 fn validate(&self) -> Result<(), ConstraintError> {
123 if let Some(ref value) = self.cover {
124 {
125 let mime = value.blob().mime_type.as_str();
126 let accepted: &[&str] = &["image/*"];
127 let matched = accepted
128 .iter()
129 .any(|pattern| {
130 if *pattern == "*/*" {
131 true
132 } else if pattern.ends_with("/*") {
133 let prefix = &pattern[..pattern.len() - 2];
134 mime.starts_with(prefix)
135 && mime.as_bytes().get(prefix.len()) == Some(&b'/')
136 } else {
137 mime == *pattern
138 }
139 });
140 if !matched {
141 return Err(ConstraintError::BlobMimeTypeNotAccepted {
142 path: ValidationPath::from_field("cover"),
143 accepted: vec!["image/*".to_string()],
144 actual: mime.to_string(),
145 });
146 }
147 }
148 }
149 Ok(())
150 }
151}
152
153pub mod post_state {
154
155 pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
156 #[allow(unused)]
157 use ::core::marker::PhantomData;
158 mod sealed {
159 pub trait Sealed {}
160 }
161 pub trait State: sealed::Sealed {
163 type Blog;
164 type Url;
165 type Title;
166 type Blocks;
167 }
168 pub struct Empty(());
170 impl sealed::Sealed for Empty {}
171 impl State for Empty {
172 type Blog = Unset;
173 type Url = Unset;
174 type Title = Unset;
175 type Blocks = Unset;
176 }
177 pub struct SetBlog<S: State = Empty>(PhantomData<fn() -> S>);
179 impl<S: State> sealed::Sealed for SetBlog<S> {}
180 impl<S: State> State for SetBlog<S> {
181 type Blog = Set<members::blog>;
182 type Url = S::Url;
183 type Title = S::Title;
184 type Blocks = S::Blocks;
185 }
186 pub struct SetUrl<S: State = Empty>(PhantomData<fn() -> S>);
188 impl<S: State> sealed::Sealed for SetUrl<S> {}
189 impl<S: State> State for SetUrl<S> {
190 type Blog = S::Blog;
191 type Url = Set<members::url>;
192 type Title = S::Title;
193 type Blocks = S::Blocks;
194 }
195 pub struct SetTitle<S: State = Empty>(PhantomData<fn() -> S>);
197 impl<S: State> sealed::Sealed for SetTitle<S> {}
198 impl<S: State> State for SetTitle<S> {
199 type Blog = S::Blog;
200 type Url = S::Url;
201 type Title = Set<members::title>;
202 type Blocks = S::Blocks;
203 }
204 pub struct SetBlocks<S: State = Empty>(PhantomData<fn() -> S>);
206 impl<S: State> sealed::Sealed for SetBlocks<S> {}
207 impl<S: State> State for SetBlocks<S> {
208 type Blog = S::Blog;
209 type Url = S::Url;
210 type Title = S::Title;
211 type Blocks = Set<members::blocks>;
212 }
213 #[allow(non_camel_case_types)]
215 pub mod members {
216 pub struct blog(());
218 pub struct url(());
220 pub struct title(());
222 pub struct blocks(());
224 }
225}
226
227pub struct PostBuilder<'a, S: post_state::State> {
229 _state: PhantomData<fn() -> S>,
230 _fields: (
231 Option<Data<'a>>,
232 Option<StrongRef<'a>>,
233 Option<CowStr<'a>>,
234 Option<BlobRef<'a>>,
235 Option<Vec<BlobRef<'a>>>,
236 Option<Datetime>,
237 Option<Vec<CowStr<'a>>>,
238 Option<CowStr<'a>>,
239 Option<Datetime>,
240 Option<UriValue<'a>>,
241 ),
242 _lifetime: PhantomData<&'a ()>,
243}
244
245impl<'a> Post<'a> {
246 pub fn new() -> PostBuilder<'a, post_state::Empty> {
248 PostBuilder::new()
249 }
250}
251
252impl<'a> PostBuilder<'a, post_state::Empty> {
253 pub fn new() -> Self {
255 PostBuilder {
256 _state: PhantomData,
257 _fields: (None, None, None, None, None, None, None, None, None, None),
258 _lifetime: PhantomData,
259 }
260 }
261}
262
263impl<'a, S> PostBuilder<'a, S>
264where
265 S: post_state::State,
266 S::Blocks: post_state::IsUnset,
267{
268 pub fn blocks(
270 mut self,
271 value: impl Into<Data<'a>>,
272 ) -> PostBuilder<'a, post_state::SetBlocks<S>> {
273 self._fields.0 = Option::Some(value.into());
274 PostBuilder {
275 _state: PhantomData,
276 _fields: self._fields,
277 _lifetime: PhantomData,
278 }
279 }
280}
281
282impl<'a, S> PostBuilder<'a, S>
283where
284 S: post_state::State,
285 S::Blog: post_state::IsUnset,
286{
287 pub fn blog(
289 mut self,
290 value: impl Into<StrongRef<'a>>,
291 ) -> PostBuilder<'a, post_state::SetBlog<S>> {
292 self._fields.1 = Option::Some(value.into());
293 PostBuilder {
294 _state: PhantomData,
295 _fields: self._fields,
296 _lifetime: PhantomData,
297 }
298 }
299}
300
301impl<'a, S: post_state::State> PostBuilder<'a, S> {
302 pub fn body_plain(mut self, value: impl Into<Option<CowStr<'a>>>) -> Self {
304 self._fields.2 = value.into();
305 self
306 }
307 pub fn maybe_body_plain(mut self, value: Option<CowStr<'a>>) -> Self {
309 self._fields.2 = value;
310 self
311 }
312}
313
314impl<'a, S: post_state::State> PostBuilder<'a, S> {
315 pub fn cover(mut self, value: impl Into<Option<BlobRef<'a>>>) -> Self {
317 self._fields.3 = value.into();
318 self
319 }
320 pub fn maybe_cover(mut self, value: Option<BlobRef<'a>>) -> Self {
322 self._fields.3 = value;
323 self
324 }
325}
326
327impl<'a, S: post_state::State> PostBuilder<'a, S> {
328 pub fn images(mut self, value: impl Into<Option<Vec<BlobRef<'a>>>>) -> Self {
330 self._fields.4 = value.into();
331 self
332 }
333 pub fn maybe_images(mut self, value: Option<Vec<BlobRef<'a>>>) -> Self {
335 self._fields.4 = value;
336 self
337 }
338}
339
340impl<'a, S: post_state::State> PostBuilder<'a, S> {
341 pub fn published_at(mut self, value: impl Into<Option<Datetime>>) -> Self {
343 self._fields.5 = value.into();
344 self
345 }
346 pub fn maybe_published_at(mut self, value: Option<Datetime>) -> Self {
348 self._fields.5 = value;
349 self
350 }
351}
352
353impl<'a, S: post_state::State> PostBuilder<'a, S> {
354 pub fn tags(mut self, value: impl Into<Option<Vec<CowStr<'a>>>>) -> Self {
356 self._fields.6 = value.into();
357 self
358 }
359 pub fn maybe_tags(mut self, value: Option<Vec<CowStr<'a>>>) -> Self {
361 self._fields.6 = value;
362 self
363 }
364}
365
366impl<'a, S> PostBuilder<'a, S>
367where
368 S: post_state::State,
369 S::Title: post_state::IsUnset,
370{
371 pub fn title(
373 mut self,
374 value: impl Into<CowStr<'a>>,
375 ) -> PostBuilder<'a, post_state::SetTitle<S>> {
376 self._fields.7 = Option::Some(value.into());
377 PostBuilder {
378 _state: PhantomData,
379 _fields: self._fields,
380 _lifetime: PhantomData,
381 }
382 }
383}
384
385impl<'a, S: post_state::State> PostBuilder<'a, S> {
386 pub fn updated_at(mut self, value: impl Into<Option<Datetime>>) -> Self {
388 self._fields.8 = value.into();
389 self
390 }
391 pub fn maybe_updated_at(mut self, value: Option<Datetime>) -> Self {
393 self._fields.8 = value;
394 self
395 }
396}
397
398impl<'a, S> PostBuilder<'a, S>
399where
400 S: post_state::State,
401 S::Url: post_state::IsUnset,
402{
403 pub fn url(
405 mut self,
406 value: impl Into<UriValue<'a>>,
407 ) -> PostBuilder<'a, post_state::SetUrl<S>> {
408 self._fields.9 = Option::Some(value.into());
409 PostBuilder {
410 _state: PhantomData,
411 _fields: self._fields,
412 _lifetime: PhantomData,
413 }
414 }
415}
416
417impl<'a, S> PostBuilder<'a, S>
418where
419 S: post_state::State,
420 S::Blog: post_state::IsSet,
421 S::Url: post_state::IsSet,
422 S::Title: post_state::IsSet,
423 S::Blocks: post_state::IsSet,
424{
425 pub fn build(self) -> Post<'a> {
427 Post {
428 blocks: self._fields.0.unwrap(),
429 blog: self._fields.1.unwrap(),
430 body_plain: self._fields.2,
431 cover: self._fields.3,
432 images: self._fields.4,
433 published_at: self._fields.5,
434 tags: self._fields.6,
435 title: self._fields.7.unwrap(),
436 updated_at: self._fields.8,
437 url: self._fields.9.unwrap(),
438 extra_data: Default::default(),
439 }
440 }
441 pub fn build_with_data(
443 self,
444 extra_data: BTreeMap<jacquard_common::deps::smol_str::SmolStr, Data<'a>>,
445 ) -> Post<'a> {
446 Post {
447 blocks: self._fields.0.unwrap(),
448 blog: self._fields.1.unwrap(),
449 body_plain: self._fields.2,
450 cover: self._fields.3,
451 images: self._fields.4,
452 published_at: self._fields.5,
453 tags: self._fields.6,
454 title: self._fields.7.unwrap(),
455 updated_at: self._fields.8,
456 url: self._fields.9.unwrap(),
457 extra_data: Some(extra_data),
458 }
459 }
460}
461
462fn lexicon_doc_blog_pckt_post() -> LexiconDoc<'static> {
463 #[allow(unused_imports)]
464 use jacquard_common::{CowStr, deps::smol_str::SmolStr, types::blob::MimeType};
465 use jacquard_lexicon::lexicon::*;
466 use alloc::collections::BTreeMap;
467 LexiconDoc {
468 lexicon: Lexicon::Lexicon1,
469 id: CowStr::new_static("blog.pckt.post"),
470 defs: {
471 let mut map = BTreeMap::new();
472 map.insert(
473 SmolStr::new_static("main"),
474 LexUserType::Record(LexRecord {
475 key: Some(CowStr::new_static("tid")),
476 record: LexRecordRecord::Object(LexObject {
477 required: Some(
478 vec![
479 SmolStr::new_static("title"), SmolStr::new_static("blocks"),
480 SmolStr::new_static("url"), SmolStr::new_static("blog")
481 ],
482 ),
483 properties: {
484 #[allow(unused_mut)]
485 let mut map = BTreeMap::new();
486 map.insert(
487 SmolStr::new_static("blocks"),
488 LexObjectProperty::Union(LexRefUnion {
489 refs: vec![],
490 closed: Some(false),
491 ..Default::default()
492 }),
493 );
494 map.insert(
495 SmolStr::new_static("blog"),
496 LexObjectProperty::Ref(LexRef {
497 r#ref: CowStr::new_static("com.atproto.repo.strongRef"),
498 ..Default::default()
499 }),
500 );
501 map.insert(
502 SmolStr::new_static("bodyPlain"),
503 LexObjectProperty::String(LexString {
504 ..Default::default()
505 }),
506 );
507 map.insert(
508 SmolStr::new_static("cover"),
509 LexObjectProperty::Blob(LexBlob { ..Default::default() }),
510 );
511 map.insert(
512 SmolStr::new_static("images"),
513 LexObjectProperty::Array(LexArray {
514 items: LexArrayItem::Blob(LexBlob { ..Default::default() }),
515 ..Default::default()
516 }),
517 );
518 map.insert(
519 SmolStr::new_static("publishedAt"),
520 LexObjectProperty::String(LexString {
521 format: Some(LexStringFormat::Datetime),
522 ..Default::default()
523 }),
524 );
525 map.insert(
526 SmolStr::new_static("tags"),
527 LexObjectProperty::Array(LexArray {
528 items: LexArrayItem::String(LexString {
529 ..Default::default()
530 }),
531 ..Default::default()
532 }),
533 );
534 map.insert(
535 SmolStr::new_static("title"),
536 LexObjectProperty::String(LexString {
537 ..Default::default()
538 }),
539 );
540 map.insert(
541 SmolStr::new_static("updatedAt"),
542 LexObjectProperty::String(LexString {
543 format: Some(LexStringFormat::Datetime),
544 ..Default::default()
545 }),
546 );
547 map.insert(
548 SmolStr::new_static("url"),
549 LexObjectProperty::String(LexString {
550 format: Some(LexStringFormat::Uri),
551 ..Default::default()
552 }),
553 );
554 map
555 },
556 ..Default::default()
557 }),
558 ..Default::default()
559 }),
560 );
561 map
562 },
563 ..Default::default()
564 }
565}