1#[allow(unused_imports)]
9use alloc::collections::BTreeMap;
10
11#[allow(unused_imports)]
12use core::marker::PhantomData;
13use jacquard_common::{BosStr, CowStr, 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::{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, open_union};
24use jacquard_lexicon::lexicon::LexiconDoc;
25use jacquard_lexicon::schema::LexiconSchema;
26
27use crate::com_atproto::repo::strong_ref::StrongRef;
28use crate::games_gamesgamesgamesgames::Signature;
29#[allow(unused_imports)]
30use jacquard_lexicon::validation::{ConstraintError, ValidationPath};
31use serde::{Deserialize, Serialize};
32#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
35#[serde(
36 rename_all = "camelCase",
37 rename = "games.gamesgamesgamesgames.contribution",
38 tag = "$type",
39 bound(deserialize = "S: Deserialize<'de> + BosStr")
40)]
41pub struct Contribution<S: BosStr = DefaultStr> {
42 pub changes: Data<S>,
44 pub contribution_type: ContributionContributionType<S>,
45 pub created_at: Datetime,
46 #[serde(skip_serializing_if = "Option::is_none")]
48 pub message: Option<S>,
49 #[serde(skip_serializing_if = "Option::is_none")]
51 pub signatures: Option<Vec<ContributionSignaturesItem<S>>>,
52 #[serde(skip_serializing_if = "Option::is_none")]
54 pub subject: Option<AtUri<S>>,
55 #[serde(flatten, default, skip_serializing_if = "Option::is_none")]
56 pub extra_data: Option<BTreeMap<SmolStr, Data<S>>>,
57}
58
59#[derive(Debug, Clone, PartialEq, Eq, Hash)]
60pub enum ContributionContributionType<S: BosStr = DefaultStr> {
61 Correction,
62 Addition,
63 NewGame,
64 Other(S),
65}
66
67impl<S: BosStr> ContributionContributionType<S> {
68 pub fn as_str(&self) -> &str {
69 match self {
70 Self::Correction => "correction",
71 Self::Addition => "addition",
72 Self::NewGame => "newGame",
73 Self::Other(s) => s.as_ref(),
74 }
75 }
76 pub fn from_value(s: S) -> Self {
78 match s.as_ref() {
79 "correction" => Self::Correction,
80 "addition" => Self::Addition,
81 "newGame" => Self::NewGame,
82 _ => Self::Other(s),
83 }
84 }
85}
86
87impl<S: BosStr> core::fmt::Display for ContributionContributionType<S> {
88 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
89 write!(f, "{}", self.as_str())
90 }
91}
92
93impl<S: BosStr> AsRef<str> for ContributionContributionType<S> {
94 fn as_ref(&self) -> &str {
95 self.as_str()
96 }
97}
98
99impl<S: BosStr> Serialize for ContributionContributionType<S> {
100 fn serialize<Ser>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error>
101 where
102 Ser: serde::Serializer,
103 {
104 serializer.serialize_str(self.as_str())
105 }
106}
107
108impl<'de, S: Deserialize<'de> + BosStr> Deserialize<'de> for ContributionContributionType<S> {
109 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
110 where
111 D: serde::Deserializer<'de>,
112 {
113 let s = S::deserialize(deserializer)?;
114 Ok(Self::from_value(s))
115 }
116}
117
118impl<S: BosStr + Default> Default for ContributionContributionType<S> {
119 fn default() -> Self {
120 Self::Other(Default::default())
121 }
122}
123
124impl<S: BosStr> jacquard_common::IntoStatic for ContributionContributionType<S>
125where
126 S: BosStr + jacquard_common::IntoStatic,
127 S::Output: BosStr,
128{
129 type Output = ContributionContributionType<S::Output>;
130 fn into_static(self) -> Self::Output {
131 match self {
132 ContributionContributionType::Correction => ContributionContributionType::Correction,
133 ContributionContributionType::Addition => ContributionContributionType::Addition,
134 ContributionContributionType::NewGame => ContributionContributionType::NewGame,
135 ContributionContributionType::Other(v) => {
136 ContributionContributionType::Other(v.into_static())
137 }
138 }
139 }
140}
141
142#[open_union]
143#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
144#[serde(tag = "$type", bound(deserialize = "S: Deserialize<'de> + BosStr"))]
145pub enum ContributionSignaturesItem<S: BosStr = DefaultStr> {
146 #[serde(rename = "games.gamesgamesgamesgames.defs#signature")]
147 Signature(Box<Signature<S>>),
148 #[serde(rename = "com.atproto.repo.strongRef")]
149 StrongRef(Box<StrongRef<S>>),
150}
151
152#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
155#[serde(rename_all = "camelCase")]
156pub struct ContributionGetRecordOutput<S: BosStr = DefaultStr> {
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub cid: Option<Cid<S>>,
159 pub uri: AtUri<S>,
160 pub value: Contribution<S>,
161}
162
163impl<S: BosStr> Contribution<S> {
164 pub fn uri(uri: S) -> Result<RecordUri<S, ContributionRecord>, UriError> {
165 RecordUri::try_from_uri(AtUri::new(uri)?)
166 }
167}
168
169#[derive(Debug, Serialize, Deserialize)]
172pub struct ContributionRecord;
173impl XrpcResp for ContributionRecord {
174 const NSID: &'static str = "games.gamesgamesgamesgames.contribution";
175 const ENCODING: &'static str = "application/json";
176 type Output<S: BosStr> = ContributionGetRecordOutput<S>;
177 type Err = RecordError;
178}
179
180impl<S: BosStr> From<ContributionGetRecordOutput<S>> for Contribution<S> {
181 fn from(output: ContributionGetRecordOutput<S>) -> Self {
182 output.value
183 }
184}
185
186impl<S: BosStr> Collection for Contribution<S> {
187 const NSID: &'static str = "games.gamesgamesgamesgames.contribution";
188 type Record = ContributionRecord;
189}
190
191impl Collection for ContributionRecord {
192 const NSID: &'static str = "games.gamesgamesgamesgames.contribution";
193 type Record = ContributionRecord;
194}
195
196impl<S: BosStr> LexiconSchema for Contribution<S> {
197 fn nsid() -> &'static str {
198 "games.gamesgamesgamesgames.contribution"
199 }
200 fn def_name() -> &'static str {
201 "main"
202 }
203 fn lexicon_doc() -> LexiconDoc<'static> {
204 lexicon_doc_games_gamesgamesgamesgames_contribution()
205 }
206 fn validate(&self) -> Result<(), ConstraintError> {
207 if let Some(ref value) = self.message {
208 {
209 let count = UnicodeSegmentation::graphemes(value.as_ref(), true).count();
210 if count > 3000usize {
211 return Err(ConstraintError::MaxGraphemes {
212 path: ValidationPath::from_field("message"),
213 max: 3000usize,
214 actual: count,
215 });
216 }
217 }
218 }
219 Ok(())
220 }
221}
222
223pub mod contribution_state {
224
225 pub use crate::builder_types::{IsSet, IsUnset, Set, Unset};
226 #[allow(unused)]
227 use ::core::marker::PhantomData;
228 mod sealed {
229 pub trait Sealed {}
230 }
231 pub trait State: sealed::Sealed {
233 type Changes;
234 type ContributionType;
235 type CreatedAt;
236 }
237 pub struct Empty(());
239 impl sealed::Sealed for Empty {}
240 impl State for Empty {
241 type Changes = Unset;
242 type ContributionType = Unset;
243 type CreatedAt = Unset;
244 }
245 pub struct SetChanges<St: State = Empty>(PhantomData<fn() -> St>);
247 impl<St: State> sealed::Sealed for SetChanges<St> {}
248 impl<St: State> State for SetChanges<St> {
249 type Changes = Set<members::changes>;
250 type ContributionType = St::ContributionType;
251 type CreatedAt = St::CreatedAt;
252 }
253 pub struct SetContributionType<St: State = Empty>(PhantomData<fn() -> St>);
255 impl<St: State> sealed::Sealed for SetContributionType<St> {}
256 impl<St: State> State for SetContributionType<St> {
257 type Changes = St::Changes;
258 type ContributionType = Set<members::contribution_type>;
259 type CreatedAt = St::CreatedAt;
260 }
261 pub struct SetCreatedAt<St: State = Empty>(PhantomData<fn() -> St>);
263 impl<St: State> sealed::Sealed for SetCreatedAt<St> {}
264 impl<St: State> State for SetCreatedAt<St> {
265 type Changes = St::Changes;
266 type ContributionType = St::ContributionType;
267 type CreatedAt = Set<members::created_at>;
268 }
269 #[allow(non_camel_case_types)]
271 pub mod members {
272 pub struct changes(());
274 pub struct contribution_type(());
276 pub struct created_at(());
278 }
279}
280
281pub struct ContributionBuilder<St: contribution_state::State, S: BosStr = DefaultStr> {
283 _state: PhantomData<fn() -> St>,
284 _fields: (
285 Option<Data<S>>,
286 Option<ContributionContributionType<S>>,
287 Option<Datetime>,
288 Option<S>,
289 Option<Vec<ContributionSignaturesItem<S>>>,
290 Option<AtUri<S>>,
291 ),
292 _type: PhantomData<fn() -> S>,
293}
294
295impl Contribution<DefaultStr> {
296 pub fn new() -> ContributionBuilder<contribution_state::Empty, DefaultStr> {
298 ContributionBuilder::new()
299 }
300}
301
302impl<S: BosStr> Contribution<S> {
303 pub fn builder() -> ContributionBuilder<contribution_state::Empty, S> {
305 ContributionBuilder::builder()
306 }
307}
308
309impl ContributionBuilder<contribution_state::Empty, DefaultStr> {
310 pub fn new() -> Self {
312 ContributionBuilder {
313 _state: PhantomData,
314 _fields: (None, None, None, None, None, None),
315 _type: PhantomData,
316 }
317 }
318}
319
320impl<S: BosStr> ContributionBuilder<contribution_state::Empty, S> {
321 pub fn builder() -> Self {
323 ContributionBuilder {
324 _state: PhantomData,
325 _fields: (None, None, None, None, None, None),
326 _type: PhantomData,
327 }
328 }
329}
330
331impl<St, S: BosStr> ContributionBuilder<St, S>
332where
333 St: contribution_state::State,
334 St::Changes: contribution_state::IsUnset,
335{
336 pub fn changes(
338 mut self,
339 value: impl Into<Data<S>>,
340 ) -> ContributionBuilder<contribution_state::SetChanges<St>, S> {
341 self._fields.0 = Option::Some(value.into());
342 ContributionBuilder {
343 _state: PhantomData,
344 _fields: self._fields,
345 _type: PhantomData,
346 }
347 }
348}
349
350impl<St, S: BosStr> ContributionBuilder<St, S>
351where
352 St: contribution_state::State,
353 St::ContributionType: contribution_state::IsUnset,
354{
355 pub fn contribution_type(
357 mut self,
358 value: impl Into<ContributionContributionType<S>>,
359 ) -> ContributionBuilder<contribution_state::SetContributionType<St>, S> {
360 self._fields.1 = Option::Some(value.into());
361 ContributionBuilder {
362 _state: PhantomData,
363 _fields: self._fields,
364 _type: PhantomData,
365 }
366 }
367}
368
369impl<St, S: BosStr> ContributionBuilder<St, S>
370where
371 St: contribution_state::State,
372 St::CreatedAt: contribution_state::IsUnset,
373{
374 pub fn created_at(
376 mut self,
377 value: impl Into<Datetime>,
378 ) -> ContributionBuilder<contribution_state::SetCreatedAt<St>, S> {
379 self._fields.2 = Option::Some(value.into());
380 ContributionBuilder {
381 _state: PhantomData,
382 _fields: self._fields,
383 _type: PhantomData,
384 }
385 }
386}
387
388impl<St: contribution_state::State, S: BosStr> ContributionBuilder<St, S> {
389 pub fn message(mut self, value: impl Into<Option<S>>) -> Self {
391 self._fields.3 = value.into();
392 self
393 }
394 pub fn maybe_message(mut self, value: Option<S>) -> Self {
396 self._fields.3 = value;
397 self
398 }
399}
400
401impl<St: contribution_state::State, S: BosStr> ContributionBuilder<St, S> {
402 pub fn signatures(
404 mut self,
405 value: impl Into<Option<Vec<ContributionSignaturesItem<S>>>>,
406 ) -> Self {
407 self._fields.4 = value.into();
408 self
409 }
410 pub fn maybe_signatures(mut self, value: Option<Vec<ContributionSignaturesItem<S>>>) -> Self {
412 self._fields.4 = value;
413 self
414 }
415}
416
417impl<St: contribution_state::State, S: BosStr> ContributionBuilder<St, S> {
418 pub fn subject(mut self, value: impl Into<Option<AtUri<S>>>) -> Self {
420 self._fields.5 = value.into();
421 self
422 }
423 pub fn maybe_subject(mut self, value: Option<AtUri<S>>) -> Self {
425 self._fields.5 = value;
426 self
427 }
428}
429
430impl<St, S: BosStr> ContributionBuilder<St, S>
431where
432 St: contribution_state::State,
433 St::Changes: contribution_state::IsSet,
434 St::ContributionType: contribution_state::IsSet,
435 St::CreatedAt: contribution_state::IsSet,
436{
437 pub fn build(self) -> Contribution<S> {
439 Contribution {
440 changes: self._fields.0.unwrap(),
441 contribution_type: self._fields.1.unwrap(),
442 created_at: self._fields.2.unwrap(),
443 message: self._fields.3,
444 signatures: self._fields.4,
445 subject: self._fields.5,
446 extra_data: Default::default(),
447 }
448 }
449 pub fn build_with_data(self, extra_data: BTreeMap<SmolStr, Data<S>>) -> Contribution<S> {
451 Contribution {
452 changes: self._fields.0.unwrap(),
453 contribution_type: self._fields.1.unwrap(),
454 created_at: self._fields.2.unwrap(),
455 message: self._fields.3,
456 signatures: self._fields.4,
457 subject: self._fields.5,
458 extra_data: Some(extra_data),
459 }
460 }
461}
462
463fn lexicon_doc_games_gamesgamesgamesgames_contribution() -> LexiconDoc<'static> {
464 use alloc::collections::BTreeMap;
465 #[allow(unused_imports)]
466 use jacquard_common::{CowStr, deps::smol_str::SmolStr, types::blob::MimeType};
467 use jacquard_lexicon::lexicon::*;
468 LexiconDoc {
469 lexicon: Lexicon::Lexicon1,
470 id: CowStr::new_static("games.gamesgamesgamesgames.contribution"),
471 defs: {
472 let mut map = BTreeMap::new();
473 map.insert(
474 SmolStr::new_static("main"),
475 LexUserType::Record(LexRecord {
476 description: Some(
477 CowStr::new_static(
478 "A community contribution proposing changes to a game or submitting a new game.",
479 ),
480 ),
481 key: Some(CowStr::new_static("tid")),
482 record: LexRecordRecord::Object(LexObject {
483 required: Some(
484 vec![
485 SmolStr::new_static("contributionType"),
486 SmolStr::new_static("changes"),
487 SmolStr::new_static("createdAt")
488 ],
489 ),
490 properties: {
491 #[allow(unused_mut)]
492 let mut map = BTreeMap::new();
493 map.insert(
494 SmolStr::new_static("changes"),
495 LexObjectProperty::Unknown(LexUnknown {
496 ..Default::default()
497 }),
498 );
499 map.insert(
500 SmolStr::new_static("contributionType"),
501 LexObjectProperty::String(LexString {
502 ..Default::default()
503 }),
504 );
505 map.insert(
506 SmolStr::new_static("createdAt"),
507 LexObjectProperty::String(LexString {
508 format: Some(LexStringFormat::Datetime),
509 ..Default::default()
510 }),
511 );
512 map.insert(
513 SmolStr::new_static("message"),
514 LexObjectProperty::String(LexString {
515 description: Some(
516 CowStr::new_static(
517 "Explanation of what is being changed and why.",
518 ),
519 ),
520 max_graphemes: Some(3000usize),
521 ..Default::default()
522 }),
523 );
524 map.insert(
525 SmolStr::new_static("signatures"),
526 LexObjectProperty::Array(LexArray {
527 description: Some(
528 CowStr::new_static(
529 "Inline attestation signatures from the AppView that created this contribution.",
530 ),
531 ),
532 items: LexArrayItem::Union(LexRefUnion {
533 refs: vec![
534 CowStr::new_static("games.gamesgamesgamesgames.defs#signature"),
535 CowStr::new_static("com.atproto.repo.strongRef")
536 ],
537 ..Default::default()
538 }),
539 ..Default::default()
540 }),
541 );
542 map.insert(
543 SmolStr::new_static("subject"),
544 LexObjectProperty::String(LexString {
545 description: Some(
546 CowStr::new_static(
547 "The entity being modified. Required for corrections and additions, absent for new game submissions.",
548 ),
549 ),
550 format: Some(LexStringFormat::AtUri),
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}