1#[allow(unused_imports)]
9use alloc::collections::BTreeMap;
10
11#[allow(unused_imports)]
12use core::marker::PhantomData;
13use jacquard_common::{CowStr, BosStr, DefaultStr, FromStaticStr};
14
15#[allow(unused_imports)]
16use jacquard_common::deps::codegen::unicode_segmentation::UnicodeSegmentation;
17use jacquard_common::deps::smol_str::SmolStr;
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::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;
31use crate::place_stream::richtext::facet::Facet;
32use crate::place_stream::chat::message;
33#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
36#[serde(
37 rename_all = "camelCase",
38 rename = "place.stream.chat.message",
39 tag = "$type",
40 bound(deserialize = "S: Deserialize<'de> + BosStr")
41)]
42pub struct Message<S: BosStr = DefaultStr> {
43 pub created_at: Datetime,
45 #[serde(skip_serializing_if = "Option::is_none")]
47 pub facets: Option<Vec<Facet<S>>>,
48 #[serde(skip_serializing_if = "Option::is_none")]
49 pub reply: Option<message::ReplyRef<S>>,
50 pub streamer: Did<S>,
52 pub text: S,
54 #[serde(flatten, default, skip_serializing_if = "Option::is_none")]
55 pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
56}
57
58#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
61#[serde(rename_all = "camelCase")]
62pub struct MessageGetRecordOutput<S: BosStr = DefaultStr> {
63 #[serde(skip_serializing_if = "Option::is_none")]
64 pub cid: Option<Cid<S>>,
65 pub uri: AtUri<S>,
66 pub value: Message<S>,
67}
68
69
70#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
71#[serde(rename_all = "camelCase", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
72pub struct ReplyRef<S: BosStr = DefaultStr> {
73 pub parent: StrongRef<S>,
74 pub root: StrongRef<S>,
75 #[serde(flatten, default, skip_serializing_if = "Option::is_none")]
76 pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
77}
78
79impl<S: BosStr> Message<S> {
80 pub fn uri(uri: S) -> Result<RecordUri<S, MessageRecord>, UriError> {
81 RecordUri::try_from_uri(AtUri::new(uri)?)
82 }
83}
84
85#[derive(Debug, Serialize, Deserialize)]
88pub struct MessageRecord;
89impl XrpcResp for MessageRecord {
90 const NSID: &'static str = "place.stream.chat.message";
91 const ENCODING: &'static str = "application/json";
92 type Output<S: BosStr> = MessageGetRecordOutput<S>;
93 type Err = RecordError;
94}
95
96impl<S: BosStr> From<MessageGetRecordOutput<S>> for Message<S> {
97 fn from(output: MessageGetRecordOutput<S>) -> Self {
98 output.value
99 }
100}
101
102impl<S: BosStr> Collection for Message<S> {
103 const NSID: &'static str = "place.stream.chat.message";
104 type Record = MessageRecord;
105}
106
107impl Collection for MessageRecord {
108 const NSID: &'static str = "place.stream.chat.message";
109 type Record = MessageRecord;
110}
111
112impl<S: BosStr> LexiconSchema for Message<S> {
113 fn nsid() -> &'static str {
114 "place.stream.chat.message"
115 }
116 fn def_name() -> &'static str {
117 "main"
118 }
119 fn lexicon_doc() -> LexiconDoc<'static> {
120 lexicon_doc_place_stream_chat_message()
121 }
122 fn validate(&self) -> Result<(), ConstraintError> {
123 {
124 let value = &self.text;
125 #[allow(unused_comparisons)]
126 if <str>::len(value.as_ref()) > 3000usize {
127 return Err(ConstraintError::MaxLength {
128 path: ValidationPath::from_field("text"),
129 max: 3000usize,
130 actual: <str>::len(value.as_ref()),
131 });
132 }
133 }
134 {
135 let value = &self.text;
136 {
137 let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
138 if count > 300usize {
139 return Err(ConstraintError::MaxGraphemes {
140 path: ValidationPath::from_field("text"),
141 max: 300usize,
142 actual: count,
143 });
144 }
145 }
146 }
147 Ok(())
148 }
149}
150
151impl<S: BosStr> LexiconSchema for ReplyRef<S> {
152 fn nsid() -> &'static str {
153 "place.stream.chat.message"
154 }
155 fn def_name() -> &'static str {
156 "replyRef"
157 }
158 fn lexicon_doc() -> LexiconDoc<'static> {
159 lexicon_doc_place_stream_chat_message()
160 }
161 fn validate(&self) -> Result<(), ConstraintError> {
162 Ok(())
163 }
164}
165
166pub mod message_state {
167
168 pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
169 #[allow(unused)]
170 use ::core::marker::PhantomData;
171 mod sealed {
172 pub trait Sealed {}
173 }
174 pub trait State: sealed::Sealed {
176 type CreatedAt;
177 type Streamer;
178 type Text;
179 }
180 pub struct Empty(());
182 impl sealed::Sealed for Empty {}
183 impl State for Empty {
184 type CreatedAt = Unset;
185 type Streamer = Unset;
186 type Text = Unset;
187 }
188 pub struct SetCreatedAt<St: State = Empty>(PhantomData<fn() -> St>);
190 impl<St: State> sealed::Sealed for SetCreatedAt<St> {}
191 impl<St: State> State for SetCreatedAt<St> {
192 type CreatedAt = Set<members::created_at>;
193 type Streamer = St::Streamer;
194 type Text = St::Text;
195 }
196 pub struct SetStreamer<St: State = Empty>(PhantomData<fn() -> St>);
198 impl<St: State> sealed::Sealed for SetStreamer<St> {}
199 impl<St: State> State for SetStreamer<St> {
200 type CreatedAt = St::CreatedAt;
201 type Streamer = Set<members::streamer>;
202 type Text = St::Text;
203 }
204 pub struct SetText<St: State = Empty>(PhantomData<fn() -> St>);
206 impl<St: State> sealed::Sealed for SetText<St> {}
207 impl<St: State> State for SetText<St> {
208 type CreatedAt = St::CreatedAt;
209 type Streamer = St::Streamer;
210 type Text = Set<members::text>;
211 }
212 #[allow(non_camel_case_types)]
214 pub mod members {
215 pub struct created_at(());
217 pub struct streamer(());
219 pub struct text(());
221 }
222}
223
224pub struct MessageBuilder<St: message_state::State, S: BosStr = DefaultStr> {
226 _state: PhantomData<fn() -> St>,
227 _fields: (
228 Option<Datetime>,
229 Option<Vec<Facet<S>>>,
230 Option<message::ReplyRef<S>>,
231 Option<Did<S>>,
232 Option<S>,
233 ),
234 _type: PhantomData<fn() -> S>,
235}
236
237impl Message<DefaultStr> {
238 pub fn new() -> MessageBuilder<message_state::Empty, DefaultStr> {
240 MessageBuilder::new()
241 }
242}
243
244impl<S: BosStr> Message<S> {
245 pub fn builder() -> MessageBuilder<message_state::Empty, S> {
247 MessageBuilder::builder()
248 }
249}
250
251impl MessageBuilder<message_state::Empty, DefaultStr> {
252 pub fn new() -> Self {
254 MessageBuilder {
255 _state: PhantomData,
256 _fields: (None, None, None, None, None),
257 _type: PhantomData,
258 }
259 }
260}
261
262impl<S: BosStr> MessageBuilder<message_state::Empty, S> {
263 pub fn builder() -> Self {
265 MessageBuilder {
266 _state: PhantomData,
267 _fields: (None, None, None, None, None),
268 _type: PhantomData,
269 }
270 }
271}
272
273impl<St, S: BosStr> MessageBuilder<St, S>
274where
275 St: message_state::State,
276 St::CreatedAt: message_state::IsUnset,
277{
278 pub fn created_at(
280 mut self,
281 value: impl Into<Datetime>,
282 ) -> MessageBuilder<message_state::SetCreatedAt<St>, S> {
283 self._fields.0 = Option::Some(value.into());
284 MessageBuilder {
285 _state: PhantomData,
286 _fields: self._fields,
287 _type: PhantomData,
288 }
289 }
290}
291
292impl<St: message_state::State, S: BosStr> MessageBuilder<St, S> {
293 pub fn facets(mut self, value: impl Into<Option<Vec<Facet<S>>>>) -> Self {
295 self._fields.1 = value.into();
296 self
297 }
298 pub fn maybe_facets(mut self, value: Option<Vec<Facet<S>>>) -> Self {
300 self._fields.1 = value;
301 self
302 }
303}
304
305impl<St: message_state::State, S: BosStr> MessageBuilder<St, S> {
306 pub fn reply(mut self, value: impl Into<Option<message::ReplyRef<S>>>) -> Self {
308 self._fields.2 = value.into();
309 self
310 }
311 pub fn maybe_reply(mut self, value: Option<message::ReplyRef<S>>) -> Self {
313 self._fields.2 = value;
314 self
315 }
316}
317
318impl<St, S: BosStr> MessageBuilder<St, S>
319where
320 St: message_state::State,
321 St::Streamer: message_state::IsUnset,
322{
323 pub fn streamer(
325 mut self,
326 value: impl Into<Did<S>>,
327 ) -> MessageBuilder<message_state::SetStreamer<St>, S> {
328 self._fields.3 = Option::Some(value.into());
329 MessageBuilder {
330 _state: PhantomData,
331 _fields: self._fields,
332 _type: PhantomData,
333 }
334 }
335}
336
337impl<St, S: BosStr> MessageBuilder<St, S>
338where
339 St: message_state::State,
340 St::Text: message_state::IsUnset,
341{
342 pub fn text(
344 mut self,
345 value: impl Into<S>,
346 ) -> MessageBuilder<message_state::SetText<St>, S> {
347 self._fields.4 = Option::Some(value.into());
348 MessageBuilder {
349 _state: PhantomData,
350 _fields: self._fields,
351 _type: PhantomData,
352 }
353 }
354}
355
356impl<St, S: BosStr> MessageBuilder<St, S>
357where
358 St: message_state::State,
359 St::CreatedAt: message_state::IsSet,
360 St::Streamer: message_state::IsSet,
361 St::Text: message_state::IsSet,
362{
363 pub fn build(self) -> Message<S> {
365 Message {
366 created_at: self._fields.0.unwrap(),
367 facets: self._fields.1,
368 reply: self._fields.2,
369 streamer: self._fields.3.unwrap(),
370 text: self._fields.4.unwrap(),
371 extra_data: Default::default(),
372 }
373 }
374 pub fn build_with_data(self, extra_data: BTreeMap<SmolStr, Data<S>>) -> Message<S> {
376 Message {
377 created_at: self._fields.0.unwrap(),
378 facets: self._fields.1,
379 reply: self._fields.2,
380 streamer: self._fields.3.unwrap(),
381 text: self._fields.4.unwrap(),
382 extra_data: Some(extra_data),
383 }
384 }
385}
386
387fn lexicon_doc_place_stream_chat_message() -> LexiconDoc<'static> {
388 #[allow(unused_imports)]
389 use jacquard_common::{CowStr, deps::smol_str::SmolStr, types::blob::MimeType};
390 use jacquard_lexicon::lexicon::*;
391 use alloc::collections::BTreeMap;
392 LexiconDoc {
393 lexicon: Lexicon::Lexicon1,
394 id: CowStr::new_static("place.stream.chat.message"),
395 defs: {
396 let mut map = BTreeMap::new();
397 map.insert(
398 SmolStr::new_static("main"),
399 LexUserType::Record(LexRecord {
400 description: Some(
401 CowStr::new_static(
402 "Record containing a Streamplace chat message.",
403 ),
404 ),
405 key: Some(CowStr::new_static("tid")),
406 record: LexRecordRecord::Object(LexObject {
407 required: Some(
408 vec![
409 SmolStr::new_static("text"),
410 SmolStr::new_static("createdAt"),
411 SmolStr::new_static("streamer")
412 ],
413 ),
414 properties: {
415 #[allow(unused_mut)]
416 let mut map = BTreeMap::new();
417 map.insert(
418 SmolStr::new_static("createdAt"),
419 LexObjectProperty::String(LexString {
420 description: Some(
421 CowStr::new_static(
422 "Client-declared timestamp when this message was originally created.",
423 ),
424 ),
425 format: Some(LexStringFormat::Datetime),
426 ..Default::default()
427 }),
428 );
429 map.insert(
430 SmolStr::new_static("facets"),
431 LexObjectProperty::Array(LexArray {
432 description: Some(
433 CowStr::new_static(
434 "Annotations of text (mentions, URLs, etc)",
435 ),
436 ),
437 items: LexArrayItem::Ref(LexRef {
438 r#ref: CowStr::new_static("place.stream.richtext.facet"),
439 ..Default::default()
440 }),
441 ..Default::default()
442 }),
443 );
444 map.insert(
445 SmolStr::new_static("reply"),
446 LexObjectProperty::Ref(LexRef {
447 r#ref: CowStr::new_static("#replyRef"),
448 ..Default::default()
449 }),
450 );
451 map.insert(
452 SmolStr::new_static("streamer"),
453 LexObjectProperty::String(LexString {
454 description: Some(
455 CowStr::new_static(
456 "The DID of the streamer whose chat this is.",
457 ),
458 ),
459 format: Some(LexStringFormat::Did),
460 ..Default::default()
461 }),
462 );
463 map.insert(
464 SmolStr::new_static("text"),
465 LexObjectProperty::String(LexString {
466 description: Some(
467 CowStr::new_static(
468 "The primary message content. May be an empty string, if there are embeds.",
469 ),
470 ),
471 max_length: Some(3000usize),
472 max_graphemes: Some(300usize),
473 ..Default::default()
474 }),
475 );
476 map
477 },
478 ..Default::default()
479 }),
480 ..Default::default()
481 }),
482 );
483 map.insert(
484 SmolStr::new_static("replyRef"),
485 LexUserType::Object(LexObject {
486 required: Some(
487 vec![SmolStr::new_static("root"), SmolStr::new_static("parent")],
488 ),
489 properties: {
490 #[allow(unused_mut)]
491 let mut map = BTreeMap::new();
492 map.insert(
493 SmolStr::new_static("parent"),
494 LexObjectProperty::Ref(LexRef {
495 r#ref: CowStr::new_static("com.atproto.repo.strongRef"),
496 ..Default::default()
497 }),
498 );
499 map.insert(
500 SmolStr::new_static("root"),
501 LexObjectProperty::Ref(LexRef {
502 r#ref: CowStr::new_static("com.atproto.repo.strongRef"),
503 ..Default::default()
504 }),
505 );
506 map
507 },
508 ..Default::default()
509 }),
510 );
511 map
512 },
513 ..Default::default()
514 }
515}
516
517pub mod reply_ref_state {
518
519 pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
520 #[allow(unused)]
521 use ::core::marker::PhantomData;
522 mod sealed {
523 pub trait Sealed {}
524 }
525 pub trait State: sealed::Sealed {
527 type Parent;
528 type Root;
529 }
530 pub struct Empty(());
532 impl sealed::Sealed for Empty {}
533 impl State for Empty {
534 type Parent = Unset;
535 type Root = Unset;
536 }
537 pub struct SetParent<St: State = Empty>(PhantomData<fn() -> St>);
539 impl<St: State> sealed::Sealed for SetParent<St> {}
540 impl<St: State> State for SetParent<St> {
541 type Parent = Set<members::parent>;
542 type Root = St::Root;
543 }
544 pub struct SetRoot<St: State = Empty>(PhantomData<fn() -> St>);
546 impl<St: State> sealed::Sealed for SetRoot<St> {}
547 impl<St: State> State for SetRoot<St> {
548 type Parent = St::Parent;
549 type Root = Set<members::root>;
550 }
551 #[allow(non_camel_case_types)]
553 pub mod members {
554 pub struct parent(());
556 pub struct root(());
558 }
559}
560
561pub struct ReplyRefBuilder<St: reply_ref_state::State, S: BosStr = DefaultStr> {
563 _state: PhantomData<fn() -> St>,
564 _fields: (Option<StrongRef<S>>, Option<StrongRef<S>>),
565 _type: PhantomData<fn() -> S>,
566}
567
568impl ReplyRef<DefaultStr> {
569 pub fn new() -> ReplyRefBuilder<reply_ref_state::Empty, DefaultStr> {
571 ReplyRefBuilder::new()
572 }
573}
574
575impl<S: BosStr> ReplyRef<S> {
576 pub fn builder() -> ReplyRefBuilder<reply_ref_state::Empty, S> {
578 ReplyRefBuilder::builder()
579 }
580}
581
582impl ReplyRefBuilder<reply_ref_state::Empty, DefaultStr> {
583 pub fn new() -> Self {
585 ReplyRefBuilder {
586 _state: PhantomData,
587 _fields: (None, None),
588 _type: PhantomData,
589 }
590 }
591}
592
593impl<S: BosStr> ReplyRefBuilder<reply_ref_state::Empty, S> {
594 pub fn builder() -> Self {
596 ReplyRefBuilder {
597 _state: PhantomData,
598 _fields: (None, None),
599 _type: PhantomData,
600 }
601 }
602}
603
604impl<St, S: BosStr> ReplyRefBuilder<St, S>
605where
606 St: reply_ref_state::State,
607 St::Parent: reply_ref_state::IsUnset,
608{
609 pub fn parent(
611 mut self,
612 value: impl Into<StrongRef<S>>,
613 ) -> ReplyRefBuilder<reply_ref_state::SetParent<St>, S> {
614 self._fields.0 = Option::Some(value.into());
615 ReplyRefBuilder {
616 _state: PhantomData,
617 _fields: self._fields,
618 _type: PhantomData,
619 }
620 }
621}
622
623impl<St, S: BosStr> ReplyRefBuilder<St, S>
624where
625 St: reply_ref_state::State,
626 St::Root: reply_ref_state::IsUnset,
627{
628 pub fn root(
630 mut self,
631 value: impl Into<StrongRef<S>>,
632 ) -> ReplyRefBuilder<reply_ref_state::SetRoot<St>, S> {
633 self._fields.1 = Option::Some(value.into());
634 ReplyRefBuilder {
635 _state: PhantomData,
636 _fields: self._fields,
637 _type: PhantomData,
638 }
639 }
640}
641
642impl<St, S: BosStr> ReplyRefBuilder<St, S>
643where
644 St: reply_ref_state::State,
645 St::Parent: reply_ref_state::IsSet,
646 St::Root: reply_ref_state::IsSet,
647{
648 pub fn build(self) -> ReplyRef<S> {
650 ReplyRef {
651 parent: self._fields.0.unwrap(),
652 root: self._fields.1.unwrap(),
653 extra_data: Default::default(),
654 }
655 }
656 pub fn build_with_data(self, extra_data: BTreeMap<SmolStr, Data<S>>) -> ReplyRef<S> {
658 ReplyRef {
659 parent: self._fields.0.unwrap(),
660 root: self._fields.1.unwrap(),
661 extra_data: Some(extra_data),
662 }
663 }
664}