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