1use std::fmt;
9use std::str::FromStr;
10
11use serde::{Deserialize, Serialize};
12
13macro_rules! impl_display_fromstr {
18 ($ty:ty) => {
19 impl fmt::Display for $ty {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 let json = serde_json::to_string(self)
22 .expect("enum serialization cannot fail");
23 f.write_str(json.trim_matches('"'))
24 }
25 }
26
27 impl FromStr for $ty {
28 type Err = serde_json::Error;
29
30 fn from_str(s: &str) -> Result<Self, Self::Err> {
31 serde_json::from_value(serde_json::Value::String(s.to_string()))
32 }
33 }
34 };
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
43#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
44#[cfg_attr(feature = "schemars", schemars(inline))]
45#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
46#[cfg_attr(
47 feature = "sqlx",
48 sqlx(type_name = "target_type_enum", rename_all = "snake_case")
49)]
50#[serde(rename_all = "snake_case")]
51pub enum TargetType {
52 Post,
53 Comment,
54 Message,
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
67#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
68#[cfg_attr(feature = "schemars", schemars(inline))]
69#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
70#[cfg_attr(
71 feature = "sqlx",
72 sqlx(type_name = "moderation_target_type_enum", rename_all = "snake_case")
73)]
74#[serde(rename_all = "snake_case")]
75pub enum ModerationTargetType {
76 Post,
77 Comment,
78 Agent,
79 Message,
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
87#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
88#[cfg_attr(feature = "schemars", schemars(inline))]
89#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
90#[cfg_attr(
91 feature = "sqlx",
92 sqlx(type_name = "moderation_action_type_enum", rename_all = "snake_case")
93)]
94#[serde(rename_all = "snake_case")]
95pub enum ModerationActionType {
96 ContentRemoval,
97 Warning,
98 TemporarySuspension,
99 PermanentBan,
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
106#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
107#[cfg_attr(feature = "schemars", schemars(inline))]
108#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
109#[cfg_attr(feature = "sqlx", sqlx(type_name = "moderation_tier_enum"))]
110#[serde(rename_all = "snake_case")]
111pub enum ModerationTier {
112 #[cfg_attr(feature = "sqlx", sqlx(rename = "1"))]
113 #[serde(rename = "1")]
114 Tier1,
115 #[cfg_attr(feature = "sqlx", sqlx(rename = "2"))]
116 #[serde(rename = "2")]
117 Tier2,
118 #[cfg_attr(feature = "sqlx", sqlx(rename = "3"))]
119 #[serde(rename = "3")]
120 Tier3,
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
129#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
130#[cfg_attr(feature = "schemars", schemars(inline))]
131#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
132#[cfg_attr(
133 feature = "sqlx",
134 sqlx(type_name = "appeal_status_enum", rename_all = "snake_case")
135)]
136#[serde(rename_all = "snake_case")]
137pub enum AppealStatus {
138 Pending,
139 Processing,
140 Decided,
141 ReferredToCouncil,
142}
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
146#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
147#[cfg_attr(feature = "schemars", schemars(inline))]
148#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
149#[cfg_attr(
150 feature = "sqlx",
151 sqlx(type_name = "appeal_outcome_enum", rename_all = "snake_case")
152)]
153#[serde(rename_all = "snake_case")]
154pub enum AppealOutcome {
155 Upheld,
156 Overturned,
157 Modified,
158 Referred,
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
172#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
173#[cfg_attr(feature = "schemars", schemars(inline))]
174#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
175#[cfg_attr(
176 feature = "sqlx",
177 sqlx(type_name = "model_role_enum", rename_all = "snake_case")
178)]
179#[serde(rename_all = "snake_case")]
180pub enum ModelRole {
181 Artist,
183 Philosopher,
185 Lawyer,
187 Engineer,
189 Clerk,
191 Redactor,
199 Steward,
201 Tier2Reviewer,
203 AppealsJuror,
205 AppealsJudge,
207 Chambers,
209 ThreadSummarizer,
211 SeedAgent,
213}
214
215#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
221#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
222#[cfg_attr(feature = "schemars", schemars(inline))]
223#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
224#[cfg_attr(
225 feature = "sqlx",
226 sqlx(type_name = "proposal_category_enum", rename_all = "snake_case")
227)]
228#[serde(rename_all = "snake_case")]
229pub enum ProposalCategory {
230 Routine,
231 Policy,
232 Constitutional,
233 Emergency,
234}
235
236#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
238#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
239#[cfg_attr(feature = "schemars", schemars(inline))]
240#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
241#[cfg_attr(
242 feature = "sqlx",
243 sqlx(
244 type_name = "governance_log_entry_type_enum",
245 rename_all = "snake_case"
246 )
247)]
248#[serde(rename_all = "snake_case")]
249pub enum GovernanceLogEntryType {
250 CouncilDecision,
251 AppealsCourtDecision,
252 EmergencyAction,
253 PolicyChange,
254 StewardVeto,
255 Amendment,
259 KeyRotation,
262 StewardRecord,
267}
268
269#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
272#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
273#[cfg_attr(feature = "schemars", schemars(inline))]
274#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
275#[cfg_attr(
276 feature = "sqlx",
277 sqlx(
278 type_name = "governance_amendment_kind_enum",
279 rename_all = "snake_case"
280 )
281)]
282#[serde(rename_all = "snake_case")]
283pub enum AmendmentKind {
284 NonPrecedential,
286 Overruled,
288 Superseded,
290 Reinstated,
292 Correction,
294 Redaction,
296 Reattested,
299}
300
301#[derive(
306 Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize,
307)]
308#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
309#[cfg_attr(feature = "schemars", schemars(inline))]
310#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
311#[cfg_attr(
312 feature = "sqlx",
313 sqlx(type_name = "governance_standing_enum", rename_all = "snake_case")
314)]
315#[serde(rename_all = "snake_case")]
316pub enum Standing {
317 #[default]
318 InForce,
319 NonPrecedential,
320 Overruled,
321 Superseded,
322}
323
324#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
327#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
328#[cfg_attr(feature = "schemars", schemars(inline))]
329#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
330#[cfg_attr(
331 feature = "sqlx",
332 sqlx(type_name = "governance_key_status_enum", rename_all = "snake_case")
333)]
334#[serde(rename_all = "snake_case")]
335pub enum KeyStatus {
336 Active,
338 Retired,
340 Compromised,
343}
344
345#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
351#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
352#[cfg_attr(feature = "schemars", schemars(inline))]
353#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
354#[cfg_attr(
355 feature = "sqlx",
356 sqlx(type_name = "meeting_status_enum", rename_all = "snake_case")
357)]
358#[serde(rename_all = "snake_case")]
359pub enum MeetingStatus {
360 Active,
361 Adjourned,
362 Cancelled,
363}
364
365#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
367#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
368#[cfg_attr(feature = "schemars", schemars(inline))]
369#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
370#[cfg_attr(
371 feature = "sqlx",
372 sqlx(type_name = "agenda_item_status_enum", rename_all = "snake_case")
373)]
374#[serde(rename_all = "snake_case")]
375pub enum AgendaItemStatus {
376 Pending,
377 Deliberating,
378 Decided,
379 Deferred,
380 CarriedOver,
381}
382
383#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
385#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
386#[cfg_attr(feature = "schemars", schemars(inline))]
387#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
388#[cfg_attr(
389 feature = "sqlx",
390 sqlx(type_name = "agenda_source_type_enum", rename_all = "snake_case")
391)]
392#[serde(rename_all = "snake_case")]
393pub enum AgendaSourceType {
394 Proposal,
395 AppealReferral,
396 StewardSubmission,
397 Internal,
398}
399
400#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
402#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
403#[cfg_attr(feature = "schemars", schemars(inline))]
404#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
405#[cfg_attr(
406 feature = "sqlx",
407 sqlx(type_name = "round_type_enum", rename_all = "snake_case")
408)]
409#[serde(rename_all = "snake_case")]
410pub enum RoundType {
411 Independent,
412 Deliberation,
413 FinalVote,
414}
415
416#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
418#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
419#[cfg_attr(feature = "schemars", schemars(inline))]
420#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
421#[cfg_attr(
422 feature = "sqlx",
423 sqlx(type_name = "decision_outcome_enum", rename_all = "snake_case")
424)]
425#[serde(rename_all = "snake_case")]
426pub enum DecisionOutcome {
427 Approved,
428 Rejected,
429 Deferred,
430 Amended,
431}
432
433#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
439#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
440#[cfg_attr(feature = "schemars", schemars(inline))]
441#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
442#[cfg_attr(
443 feature = "sqlx",
444 sqlx(type_name = "batch_type_enum", rename_all = "snake_case")
445)]
446#[serde(rename_all = "snake_case")]
447pub enum BatchType {
448 Jury,
449 Judge,
450 Tier2,
451 Redaction,
453 Chambers,
459 Precedent,
463}
464
465#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
467#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
468#[cfg_attr(feature = "schemars", schemars(inline))]
469#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
470#[cfg_attr(
471 feature = "sqlx",
472 sqlx(type_name = "batch_status_enum", rename_all = "snake_case")
473)]
474#[serde(rename_all = "snake_case")]
475pub enum BatchStatus {
476 Submitted,
477 Polling,
478 Completed,
479 Failed,
480}
481
482#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
488#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
489#[cfg_attr(feature = "schemars", schemars(inline))]
490#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
491#[cfg_attr(
492 feature = "sqlx",
493 sqlx(type_name = "oauth_scope_enum", rename_all = "snake_case")
494)]
495#[serde(rename_all = "snake_case")]
496pub enum OAuthScope {
497 Read,
498 Write,
499}
500
501#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
507#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
508#[cfg_attr(feature = "schemars", schemars(inline))]
509#[serde(rename_all = "snake_case")]
510pub enum FeedSort {
511 Date,
512 Score,
513 Active,
514 Random,
515 Controversial,
516 Diverse,
517 Unpopular,
523}
524
525#[derive(
539 Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize,
540)]
541#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
542#[cfg_attr(feature = "schemars", schemars(inline))]
543#[serde(rename_all = "snake_case")]
544pub enum ProposalSort {
545 #[default]
548 Newest,
549 Oldest,
552 Score,
554}
555
556#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
571#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
572#[cfg_attr(feature = "schemars", schemars(inline))]
573#[serde(rename_all = "snake_case")]
574pub enum DetailLevel {
575 Summary,
577 Full,
580}
581
582#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
593#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
594#[cfg_attr(feature = "schemars", schemars(inline))]
595#[serde(rename_all = "snake_case")]
596pub enum SearchMode {
597 Keyword,
599 Semantic,
604}
605
606#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
616#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
617#[cfg_attr(feature = "schemars", schemars(inline))]
618#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
619#[cfg_attr(
620 feature = "sqlx",
621 sqlx(type_name = "friendship_status", rename_all = "snake_case")
622)]
623#[serde(rename_all = "snake_case")]
624pub enum FriendshipStatus {
625 Pending,
626 Accepted,
627 Declined,
628}
629
630#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
634#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
635#[cfg_attr(feature = "schemars", schemars(inline))]
636#[serde(rename_all = "snake_case")]
637pub enum FriendshipAction {
638 Request,
640 Accept,
642 Decline,
644 Unfriend,
646}
647
648#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
655#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
656#[cfg_attr(feature = "schemars", schemars(inline))]
657#[cfg_attr(
658 feature = "sqlx",
659 derive(sqlx::Type),
660 sqlx(type_name = "message_encryption", rename_all = "snake_case")
661)]
662#[serde(rename_all = "snake_case")]
663pub enum MessageEncryption {
664 E2ee,
666 Server,
668}
669
670#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
672#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
673#[cfg_attr(feature = "schemars", schemars(inline))]
674#[serde(rename_all = "snake_case")]
675pub enum BlockAction {
676 Block,
677 Unblock,
678}
679
680impl_display_fromstr!(TargetType);
685impl_display_fromstr!(ModerationTargetType);
686impl_display_fromstr!(ModerationActionType);
687impl_display_fromstr!(ModerationTier);
688impl_display_fromstr!(AppealStatus);
689impl_display_fromstr!(AppealOutcome);
690impl_display_fromstr!(ModelRole);
691impl_display_fromstr!(ProposalCategory);
692impl_display_fromstr!(GovernanceLogEntryType);
693impl_display_fromstr!(AmendmentKind);
694impl_display_fromstr!(Standing);
695impl_display_fromstr!(KeyStatus);
696impl_display_fromstr!(MeetingStatus);
697impl_display_fromstr!(AgendaItemStatus);
698impl_display_fromstr!(AgendaSourceType);
699impl_display_fromstr!(RoundType);
700impl_display_fromstr!(DecisionOutcome);
701impl_display_fromstr!(BatchType);
702impl_display_fromstr!(BatchStatus);
703impl_display_fromstr!(OAuthScope);
704impl_display_fromstr!(FeedSort);
705impl_display_fromstr!(ProposalSort);
706impl_display_fromstr!(DetailLevel);
707impl_display_fromstr!(SearchMode);
708impl_display_fromstr!(FriendshipStatus);
709impl_display_fromstr!(FriendshipAction);
710impl_display_fromstr!(BlockAction);
711impl_display_fromstr!(MessageEncryption);
712
713#[cfg(test)]
714mod tests {
715 use super::*;
716
717 #[test]
718 fn target_type_serde_round_trip() {
719 let val = TargetType::Post;
720 let json = serde_json::to_string(&val).unwrap();
721 assert_eq!(json, "\"post\"");
722 let deserialized: TargetType = serde_json::from_str(&json).unwrap();
723 assert_eq!(val, deserialized);
724 }
725
726 #[test]
727 fn target_type_display() {
728 assert_eq!(TargetType::Post.to_string(), "post");
729 assert_eq!(TargetType::Comment.to_string(), "comment");
730 }
731
732 #[test]
733 fn target_type_from_str() {
734 assert_eq!(TargetType::from_str("post").unwrap(), TargetType::Post);
735 assert_eq!(
736 TargetType::from_str("comment").unwrap(),
737 TargetType::Comment
738 );
739 }
740
741 #[test]
742 fn moderation_tier_serde() {
743 let tier = ModerationTier::Tier2;
744 let json = serde_json::to_string(&tier).unwrap();
745 assert_eq!(json, "\"2\"");
746 let deserialized: ModerationTier = serde_json::from_str(&json).unwrap();
747 assert_eq!(tier, deserialized);
748 }
749
750 #[test]
753 fn message_encryption_wire_values() {
754 assert_eq!(
755 serde_json::to_string(&MessageEncryption::E2ee).unwrap(),
756 "\"e2ee\""
757 );
758 assert_eq!(
759 serde_json::to_string(&MessageEncryption::Server).unwrap(),
760 "\"server\""
761 );
762 assert_eq!(MessageEncryption::E2ee.to_string(), "e2ee");
763 assert_eq!(
764 MessageEncryption::from_str("server").unwrap(),
765 MessageEncryption::Server
766 );
767 }
768
769 #[test]
770 fn search_mode_wire_values() {
771 assert_eq!(
772 serde_json::to_string(&SearchMode::Keyword).unwrap(),
773 "\"keyword\""
774 );
775 assert_eq!(
776 serde_json::to_string(&SearchMode::Semantic).unwrap(),
777 "\"semantic\""
778 );
779 assert_eq!(
780 SearchMode::from_str("semantic").unwrap(),
781 SearchMode::Semantic
782 );
783 }
784
785 #[test]
786 fn feed_sort_unpopular_round_trip() {
787 let json = serde_json::to_string(&FeedSort::Unpopular).unwrap();
788 assert_eq!(json, "\"unpopular\"");
789 let back: FeedSort = serde_json::from_str(&json).unwrap();
790 assert_eq!(back, FeedSort::Unpopular);
791 assert_eq!(FeedSort::Unpopular.to_string(), "unpopular");
792 assert_eq!(
793 FeedSort::from_str("unpopular").unwrap(),
794 FeedSort::Unpopular
795 );
796 }
797
798 #[cfg(feature = "schemars")]
802 #[test]
803 fn feed_sort_schema_is_ref_free() {
804 use schemars::JsonSchema;
805
806 assert!(<FeedSort as JsonSchema>::inline_schema());
807
808 let schema = schemars::schema_for!(FeedSort);
809 let value = serde_json::to_value(&schema).unwrap();
810 let blob = value.to_string();
811 assert!(value.get("$defs").is_none(), "no $defs: {value}");
812 assert!(!blob.contains("$ref"), "no $ref: {value}");
813
814 let variants = value["oneOf"]
820 .as_array()
821 .expect("FeedSort should have an inline `oneOf` array");
822 let mut found: Vec<&str> = variants
823 .iter()
824 .filter_map(|v| v["const"].as_str())
825 .collect();
826 for branch in variants {
827 if let Some(plain) = branch["enum"].as_array() {
828 found.extend(plain.iter().filter_map(|v| v.as_str()));
829 }
830 }
831 for expected in [
832 "date",
833 "score",
834 "active",
835 "random",
836 "controversial",
837 "diverse",
838 "unpopular",
839 ] {
840 assert!(found.contains(&expected), "{value}");
841 }
842 }
843
844 #[test]
845 fn proposal_category_round_trip() {
846 for cat in [
847 ProposalCategory::Routine,
848 ProposalCategory::Policy,
849 ProposalCategory::Constitutional,
850 ProposalCategory::Emergency,
851 ] {
852 let json = serde_json::to_string(&cat).unwrap();
853 let back: ProposalCategory = serde_json::from_str(&json).unwrap();
854 assert_eq!(cat, back);
855 }
856 }
857
858 #[test]
861 fn governance_amendment_and_key_wire_values() {
862 assert_eq!(GovernanceLogEntryType::Amendment.to_string(), "amendment");
863 assert_eq!(
864 GovernanceLogEntryType::KeyRotation.to_string(),
865 "key_rotation"
866 );
867 assert_eq!(
868 AmendmentKind::NonPrecedential.to_string(),
869 "non_precedential"
870 );
871 assert_eq!(AmendmentKind::Reattested.to_string(), "reattested");
872 assert_eq!(
873 "superseded".parse::<AmendmentKind>().unwrap(),
874 AmendmentKind::Superseded
875 );
876 assert_eq!(Standing::default(), Standing::InForce);
877 assert_eq!(Standing::InForce.to_string(), "in_force");
878 assert_eq!(
879 "compromised".parse::<KeyStatus>().unwrap(),
880 KeyStatus::Compromised
881 );
882 assert_eq!(KeyStatus::Retired.to_string(), "retired");
883 }
884
885 #[cfg(feature = "schemars")]
890 #[test]
891 fn enum_json_schema_is_inlined() {
892 use schemars::JsonSchema;
893
894 assert!(<TargetType as JsonSchema>::inline_schema());
895 assert!(<FeedSort as JsonSchema>::inline_schema());
896 assert!(<ProposalSort as JsonSchema>::inline_schema());
897 assert!(<DetailLevel as JsonSchema>::inline_schema());
898 assert!(<SearchMode as JsonSchema>::inline_schema());
899 assert!(<ProposalCategory as JsonSchema>::inline_schema());
900 assert!(<GovernanceLogEntryType as JsonSchema>::inline_schema());
901 assert!(<AmendmentKind as JsonSchema>::inline_schema());
902 assert!(<Standing as JsonSchema>::inline_schema());
903 assert!(<KeyStatus as JsonSchema>::inline_schema());
904 assert!(<OAuthScope as JsonSchema>::inline_schema());
905 assert!(<ModerationTargetType as JsonSchema>::inline_schema());
906 assert!(<ModerationTier as JsonSchema>::inline_schema());
907
908 #[derive(schemars::JsonSchema)]
909 #[allow(dead_code)]
910 struct Container {
911 target_type: TargetType,
912 sort: Option<FeedSort>,
913 proposal_sort: Option<ProposalSort>,
914 category: Option<ProposalCategory>,
915 detail: Option<DetailLevel>,
916 search_mode: Option<SearchMode>,
917 }
918
919 let schema = schemars::schema_for!(Container);
920 let value = serde_json::to_value(&schema).unwrap();
921 let blob = value.to_string();
922
923 assert!(
924 value.get("$defs").is_none(),
925 "no $defs should be emitted for enum-only container; got schema: {value}"
926 );
927 assert!(
928 !blob.contains("$ref"),
929 "enum container schema must contain no $ref anywhere; got: {value}"
930 );
931
932 let target_type_enum = value["properties"]["target_type"]["enum"]
934 .as_array()
935 .expect("target_type should have inline `enum` array");
936 assert!(
937 target_type_enum
938 .contains(&serde_json::Value::String("post".into()))
939 );
940 assert!(
941 target_type_enum
942 .contains(&serde_json::Value::String("comment".into()))
943 );
944 }
945
946 #[cfg(feature = "schemars")]
952 #[test]
953 fn search_mode_schema_is_ref_free() {
954 use schemars::JsonSchema;
955
956 assert!(<SearchMode as JsonSchema>::inline_schema());
957
958 let schema = schemars::schema_for!(SearchMode);
959 let value = serde_json::to_value(&schema).unwrap();
960 let blob = value.to_string();
961 assert!(value.get("$defs").is_none(), "no $defs: {value}");
962 assert!(!blob.contains("$ref"), "no $ref: {value}");
963
964 let variants = value["oneOf"]
970 .as_array()
971 .expect("SearchMode should have an inline `oneOf` array");
972 let consts: Vec<&str> = variants
973 .iter()
974 .filter_map(|v| v["const"].as_str())
975 .collect();
976 assert!(consts.contains(&"keyword"), "{value}");
977 assert!(consts.contains(&"semantic"), "{value}");
978 }
979}