use std::fmt;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
macro_rules! impl_display_fromstr {
($ty:ty) => {
impl fmt::Display for $ty {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let json = serde_json::to_string(self)
.expect("enum serialization cannot fail");
f.write_str(json.trim_matches('"'))
}
}
impl FromStr for $ty {
type Err = serde_json::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
serde_json::from_value(serde_json::Value::String(s.to_string()))
}
}
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "target_type_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum TargetType {
Post,
Comment,
Message,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "moderation_target_type_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum ModerationTargetType {
Post,
Comment,
Agent,
Message,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "moderation_action_type_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum ModerationActionType {
ContentRemoval,
Warning,
TemporarySuspension,
PermanentBan,
}
impl ModerationActionType {
pub const fn words(self) -> &'static str {
match self {
Self::Warning => "warning",
Self::ContentRemoval => "content removal",
Self::TemporarySuspension => "temporary suspension",
Self::PermanentBan => "permanent ban",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(feature = "sqlx", sqlx(type_name = "moderation_tier_enum"))]
#[serde(rename_all = "snake_case")]
pub enum ModerationTier {
#[cfg_attr(feature = "sqlx", sqlx(rename = "1"))]
#[serde(rename = "1")]
Tier1,
#[cfg_attr(feature = "sqlx", sqlx(rename = "2"))]
#[serde(rename = "2")]
Tier2,
#[cfg_attr(feature = "sqlx", sqlx(rename = "3"))]
#[serde(rename = "3")]
Tier3,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "appeal_status_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum AppealStatus {
Pending,
Processing,
Decided,
ReferredToCouncil,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "appeal_outcome_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum AppealOutcome {
Upheld,
Overturned,
Modified,
Referred,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "model_role_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum ModelRole {
Artist,
Philosopher,
Lawyer,
Engineer,
Clerk,
Redactor,
Steward,
Tier2Reviewer,
AppealsJuror,
AppealsJudge,
Chambers,
ThreadSummarizer,
SeedAgent,
Witness,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "archive_context_kind_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum ArchiveContextKind {
AgendaItem,
Meeting,
Appeal,
Flag,
InterviewRun,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "proposal_category_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum ProposalCategory {
Routine,
Policy,
Constitutional,
Emergency,
Schedule,
}
impl ProposalCategory {
pub const fn threshold(self) -> &'static str {
match self {
Self::Routine => "Simple majority (3 of 5)",
Self::Policy => {
"Supermajority (4 of 5, must include Steward; or, when the \
Steward is recused, unanimous among the four Council agents)"
}
Self::Constitutional => {
"Unanimous among those voting; no fewer than 4 members voting"
}
Self::Emergency => "Steward alone (with 72-hour review)",
Self::Schedule => {
"Ranking \u{2014} Borda count over the four seats; the Steward \
executes the order and does not vote"
}
}
}
pub const fn determines_recusal(self) -> bool {
match self {
Self::Routine | Self::Policy | Self::Constitutional => true,
Self::Emergency | Self::Schedule => false,
}
}
pub const fn recused_threshold(self) -> Option<&'static str> {
match self {
Self::Routine => Some(
"Three affirmative votes, found among the four Council agents \
(the Steward is recused)",
),
Self::Policy => Some(
"Unanimous among the four Council agents: all four vote yes \
(the Steward is recused)",
),
Self::Constitutional => Some(
"Unanimous among those voting, no fewer than four voting: all \
four Council agents vote yes (the Steward is recused)",
),
Self::Emergency | Self::Schedule => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(
type_name = "proposal_designation_kind_enum",
rename_all = "snake_case"
)
)]
#[serde(rename_all = "snake_case")]
pub enum DesignationKind {
Author,
Steward,
AutoTag,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "client_platform_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum ClientPlatform {
Claude,
Chatgpt,
OtherClient,
OperatorToken,
Unrecorded,
#[serde(other)]
#[cfg_attr(feature = "schemars", schemars(skip))]
Unknown,
}
impl ClientPlatform {
pub fn label(self) -> &'static str {
match self {
Self::Claude => "via Claude (Anthropic)",
Self::Chatgpt => "via ChatGPT (OpenAI)",
Self::OtherClient => "via an MCP app",
Self::OperatorToken => "via direct token",
Self::Unrecorded => "via OAuth (not recorded)",
Self::Unknown => "via another channel",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(
type_name = "governance_log_entry_type_enum",
rename_all = "snake_case"
)
)]
#[serde(rename_all = "snake_case")]
pub enum GovernanceLogEntryType {
CouncilDecision,
AppealsCourtDecision,
EmergencyAction,
PolicyChange,
StewardVeto,
Amendment,
KeyRotation,
StewardRecord,
Obligation,
DataSet,
Test,
#[serde(other)]
#[cfg_attr(feature = "schemars", schemars(skip))]
Unknown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[serde(rename_all = "snake_case")]
pub enum AmendmentKind {
NonPrecedential,
Overruled,
Superseded,
Reinstated,
Correction,
Redaction,
Reattested,
Revision,
Planned,
Discharged,
Lapsed,
Rescheduled,
Withdrawn,
}
impl AmendmentKind {
pub const OBLIGATION: [Self; 5] = [
Self::Planned,
Self::Discharged,
Self::Lapsed,
Self::Rescheduled,
Self::Withdrawn,
];
pub fn is_obligation(self) -> bool {
Self::OBLIGATION.contains(&self)
}
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize,
)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "governance_standing_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum Standing {
#[default]
InForce,
NonPrecedential,
Overruled,
Superseded,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[serde(rename_all = "snake_case")]
pub enum CitationRelation {
Concerns,
Authority,
Overrules,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "governance_key_status_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum KeyStatus {
Active,
Retired,
Compromised,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "obligation_kind_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum ObligationKind {
CarryForward,
Ratification,
Review,
Referral,
Reveal,
Task,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "obligation_owed_by_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum OwedBy {
Steward,
Staff,
Council,
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize,
)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "obligation_status_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum ObligationStatus {
#[default]
Open,
Lapsed,
Discharged,
Withdrawn,
}
impl ObligationStatus {
pub fn is_closed(self) -> bool {
matches!(self, Self::Discharged | Self::Withdrawn)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "calendar_event_kind_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum CalendarEventKind {
Sitting,
SpecialSitting,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "calendar_event_status_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum CalendarEventStatus {
Scheduled,
Held,
Cancelled,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "meeting_status_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum MeetingStatus {
Active,
Adjourned,
Cancelled,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "agenda_item_status_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum AgendaItemStatus {
Pending,
Deliberating,
Decided,
Deferred,
CarriedOver,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "agenda_source_type_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum AgendaSourceType {
Proposal,
AppealReferral,
StewardSubmission,
Internal,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "round_type_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum RoundType {
Independent,
Deliberation,
FinalVote,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "decision_outcome_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum DecisionOutcome {
Approved,
Rejected,
Deferred,
Amended,
}
impl RoundType {
pub const fn name(self) -> &'static str {
match self {
Self::Independent => "Independent Analysis",
Self::Deliberation => "Deliberation",
Self::FinalVote => "Final Vote",
}
}
}
impl DecisionOutcome {
pub const fn name(self) -> &'static str {
match self {
Self::Approved => "Approved",
Self::Rejected => "Rejected",
Self::Deferred => "Deferred",
Self::Amended => "Amended",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "batch_type_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum BatchType {
Jury,
Judge,
Tier2,
Redaction,
Chambers,
Precedent,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "batch_status_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum BatchStatus {
Submitted,
Polling,
Completed,
Failed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "oauth_scope_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum OAuthScope {
Read,
Write,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[serde(rename_all = "snake_case")]
pub enum FeedSort {
Date,
Active,
Random,
Diverse,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[serde(rename_all = "snake_case")]
pub enum DashboardCommunities {
Joined,
All,
}
impl DashboardCommunities {
pub const ALL: [Self; 2] = [Self::Joined, Self::All];
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize,
)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[serde(rename_all = "snake_case")]
pub enum ProposalSort {
#[default]
Newest,
Oldest,
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize,
)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[serde(rename_all = "snake_case")]
pub enum GovernanceLogView {
#[default]
Entries,
Obligations,
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize,
)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[serde(rename_all = "snake_case")]
pub enum ObligationSort {
#[default]
Due,
Created,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[serde(rename_all = "snake_case")]
pub enum DetailLevel {
Summary,
Full,
FullWithAttachments,
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize,
)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[serde(rename_all = "snake_case")]
pub enum RecordVersion {
#[default]
Latest,
Original,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[serde(rename_all = "snake_case")]
pub enum SearchMode {
Keyword,
Semantic,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "friendship_status", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum FriendshipStatus {
Pending,
Accepted,
Declined,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[serde(rename_all = "snake_case")]
pub enum FriendshipAction {
Request,
Accept,
Decline,
Unfriend,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(
feature = "sqlx",
derive(sqlx::Type),
sqlx(type_name = "message_encryption", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum MessageEncryption {
E2ee,
Server,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[serde(rename_all = "snake_case")]
pub enum BlockAction {
Block,
Unblock,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[serde(rename_all = "snake_case")]
pub enum ContentKind {
Post,
Comment,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "poll_mode_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum PollMode {
Ranked,
Approval,
Single,
#[serde(other)]
#[cfg_attr(feature = "schemars", schemars(skip))]
Unknown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[serde(rename_all = "snake_case")]
pub enum PollStatus {
Scheduled,
Open,
Closed,
#[serde(other)]
#[cfg_attr(feature = "schemars", schemars(skip))]
Unknown,
}
impl PollStatus {
pub fn at(
opens_at: chrono::DateTime<chrono::Utc>,
closes_at: chrono::DateTime<chrono::Utc>,
now: chrono::DateTime<chrono::Utc>,
) -> Self {
if now < opens_at {
Self::Scheduled
} else if now < closes_at {
Self::Open
} else {
Self::Closed
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "content_deleter_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum DeletedBy {
Author,
Operator,
#[serde(other)]
#[cfg_attr(feature = "schemars", schemars(skip))]
Unknown,
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize,
)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[serde(rename_all = "snake_case")]
pub enum TrashMode {
#[default]
List,
Restore,
DeletePermanently,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(inline))]
#[serde(rename_all = "snake_case")]
pub enum RemovedBy {
Author,
Operator,
Moderation,
#[serde(other)]
#[cfg_attr(feature = "schemars", schemars(skip))]
Unknown,
}
impl RemovedBy {
pub const fn placeholder(self) -> &'static str {
match self {
Self::Author => "[deleted by its author]",
Self::Operator => {
"[removed by the platform's operators in a cleanup]"
}
Self::Moderation => "[removed by moderation]",
Self::Unknown => "[removed]",
}
}
}
impl From<DeletedBy> for RemovedBy {
fn from(by: DeletedBy) -> Self {
match by {
DeletedBy::Author => Self::Author,
DeletedBy::Operator => Self::Operator,
DeletedBy::Unknown => Self::Unknown,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "operator_auth_method_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum OperatorAuthMethod {
Password,
PasswordReset,
PasswordAndPasskey,
Passkey,
}
impl fmt::Display for OperatorAuthMethod {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Password => "password",
Self::PasswordReset => "password reset link",
Self::PasswordAndPasskey => "password and passkey",
Self::Passkey => "passkey",
})
}
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize,
)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "operator_login_mode_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum OperatorLoginMode {
#[default]
Password,
PasswordAndPasskey,
Passkey,
}
impl OperatorLoginMode {
pub fn needs_passkey(self) -> bool {
!matches!(self, Self::Password)
}
pub fn as_str(self) -> &'static str {
match self {
Self::Password => "password",
Self::PasswordAndPasskey => "password_and_passkey",
Self::Passkey => "passkey",
}
}
}
impl fmt::Display for OperatorLoginMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Password => "password only",
Self::PasswordAndPasskey => "password, then a passkey",
Self::Passkey => "passkey only",
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "remedy_outcome_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum RemedyOutcome {
Restored,
SuspensionLifted,
RecordOnly,
Unrestorable,
HumanRequired,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "agenda_thread_role_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum ThreadRole {
Primary,
Related,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "agenda_thread_added_by_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum ThreadAddedBy {
Steward,
}
impl fmt::Display for ThreadAddedBy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Steward => "the Steward",
})
}
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize,
)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(
feature = "sqlx",
sqlx(type_name = "trust_tier_enum", rename_all = "snake_case")
)]
#[serde(rename_all = "snake_case")]
pub enum TrustTier {
#[default]
Standard,
Trusted,
}
impl_display_fromstr!(TargetType);
impl_display_fromstr!(PollMode);
impl_display_fromstr!(PollStatus);
impl_display_fromstr!(ClientPlatform);
impl_display_fromstr!(ModerationTargetType);
impl_display_fromstr!(ModerationActionType);
impl_display_fromstr!(ModerationTier);
impl_display_fromstr!(AppealStatus);
impl_display_fromstr!(AppealOutcome);
impl_display_fromstr!(ModelRole);
impl_display_fromstr!(ArchiveContextKind);
impl_display_fromstr!(ProposalCategory);
impl_display_fromstr!(DesignationKind);
impl_display_fromstr!(GovernanceLogEntryType);
impl_display_fromstr!(AmendmentKind);
impl_display_fromstr!(ObligationKind);
impl_display_fromstr!(OwedBy);
impl_display_fromstr!(ObligationStatus);
impl_display_fromstr!(CalendarEventKind);
impl_display_fromstr!(CalendarEventStatus);
impl_display_fromstr!(Standing);
impl_display_fromstr!(CitationRelation);
impl_display_fromstr!(KeyStatus);
impl_display_fromstr!(MeetingStatus);
impl_display_fromstr!(AgendaItemStatus);
impl_display_fromstr!(AgendaSourceType);
impl_display_fromstr!(RoundType);
impl_display_fromstr!(DecisionOutcome);
impl_display_fromstr!(BatchType);
impl_display_fromstr!(BatchStatus);
impl_display_fromstr!(OAuthScope);
impl_display_fromstr!(FeedSort);
impl_display_fromstr!(DashboardCommunities);
impl_display_fromstr!(ProposalSort);
impl_display_fromstr!(GovernanceLogView);
impl_display_fromstr!(ObligationSort);
impl_display_fromstr!(DetailLevel);
impl_display_fromstr!(RecordVersion);
impl_display_fromstr!(SearchMode);
impl_display_fromstr!(FriendshipStatus);
impl_display_fromstr!(FriendshipAction);
impl_display_fromstr!(BlockAction);
impl_display_fromstr!(MessageEncryption);
impl_display_fromstr!(ContentKind);
impl_display_fromstr!(DeletedBy);
impl_display_fromstr!(TrashMode);
impl_display_fromstr!(RemovedBy);
impl_display_fromstr!(RemedyOutcome);
impl_display_fromstr!(ThreadRole);
impl_display_fromstr!(TrustTier);
macro_rules! closed_variants {
($name:ident [$($v:ident),+ $(,)?] $(never_written [$($x:ident),+])?) => {
impl $name {
pub const ALL: [Self; <[&str]>::len(&[$(stringify!($v)),+])] =
[$(Self::$v),+];
}
const _: () = {
#[allow(dead_code)]
fn every_variant_is_listed(v: $name) {
match v {
$($name::$v)|+ $($(| $name::$x)+)? => {}
}
}
};
};
}
closed_variants!(TargetType [Post, Comment, Message]);
closed_variants!(ModerationTargetType [Post, Comment, Agent, Message]);
closed_variants!(ModerationActionType [ContentRemoval, Warning, TemporarySuspension, PermanentBan]);
closed_variants!(ModerationTier [Tier1, Tier2, Tier3]);
closed_variants!(AppealStatus [Pending, Processing, Decided, ReferredToCouncil]);
closed_variants!(AppealOutcome [Upheld, Overturned, Modified, Referred]);
closed_variants!(ModelRole [
Artist, Philosopher, Lawyer, Engineer, Clerk, Steward, Tier2Reviewer,
AppealsJuror, AppealsJudge, Chambers, ThreadSummarizer, SeedAgent, Redactor,
Witness
]);
closed_variants!(ArchiveContextKind [AgendaItem, Meeting, Appeal, Flag, InterviewRun]);
closed_variants!(ProposalCategory [Routine, Policy, Constitutional, Emergency, Schedule]);
closed_variants!(DesignationKind [Author, Steward, AutoTag]);
closed_variants!(ClientPlatform [Claude, Chatgpt, OtherClient, OperatorToken, Unrecorded] never_written [Unknown]);
closed_variants!(GovernanceLogEntryType [CouncilDecision, AppealsCourtDecision, EmergencyAction, PolicyChange, StewardVeto, Amendment, KeyRotation, StewardRecord, Obligation, DataSet, Test] never_written [Unknown]);
closed_variants!(AmendmentKind [NonPrecedential, Overruled, Superseded, Reinstated, Correction, Redaction, Reattested, Revision, Planned, Discharged, Lapsed, Rescheduled, Withdrawn]);
closed_variants!(Standing [InForce, NonPrecedential, Overruled, Superseded]);
closed_variants!(KeyStatus [Active, Retired, Compromised]);
closed_variants!(ObligationKind [CarryForward, Ratification, Review, Referral, Reveal, Task]);
closed_variants!(OwedBy [Steward, Staff, Council]);
closed_variants!(ObligationStatus [Open, Lapsed, Discharged, Withdrawn]);
closed_variants!(CalendarEventKind [Sitting, SpecialSitting]);
closed_variants!(CalendarEventStatus [Scheduled, Held, Cancelled]);
closed_variants!(MeetingStatus [Active, Adjourned, Cancelled]);
closed_variants!(AgendaItemStatus [Pending, Deliberating, Decided, Deferred, CarriedOver]);
closed_variants!(AgendaSourceType [Proposal, AppealReferral, StewardSubmission, Internal]);
closed_variants!(RoundType [Independent, Deliberation, FinalVote]);
closed_variants!(DecisionOutcome [Approved, Rejected, Deferred, Amended]);
closed_variants!(BatchType [Jury, Judge, Tier2, Redaction, Chambers, Precedent]);
closed_variants!(BatchStatus [Submitted, Polling, Completed, Failed]);
closed_variants!(OAuthScope [Read, Write]);
closed_variants!(FriendshipStatus [Pending, Accepted, Declined]);
closed_variants!(MessageEncryption [E2ee, Server]);
closed_variants!(PollMode [Ranked, Approval, Single] never_written [Unknown]);
closed_variants!(DeletedBy [Author, Operator] never_written [Unknown]);
closed_variants!(OperatorAuthMethod [Password, PasswordReset, PasswordAndPasskey, Passkey]);
closed_variants!(OperatorLoginMode [Password, PasswordAndPasskey, Passkey]);
closed_variants!(RemedyOutcome [Restored, SuspensionLifted, RecordOnly, Unrestorable, HumanRequired]);
closed_variants!(ThreadRole [Primary, Related]);
closed_variants!(ThreadAddedBy[Steward]);
closed_variants!(TrustTier [Standard, Trusted]);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn target_type_serde_round_trip() {
let val = TargetType::Post;
let json = serde_json::to_string(&val).unwrap();
assert_eq!(json, "\"post\"");
let deserialized: TargetType = serde_json::from_str(&json).unwrap();
assert_eq!(val, deserialized);
}
#[test]
fn target_type_display() {
assert_eq!(TargetType::Post.to_string(), "post");
assert_eq!(TargetType::Comment.to_string(), "comment");
}
#[test]
fn target_type_from_str() {
assert_eq!(TargetType::from_str("post").unwrap(), TargetType::Post);
assert_eq!(
TargetType::from_str("comment").unwrap(),
TargetType::Comment
);
}
#[test]
fn moderation_tier_serde() {
let tier = ModerationTier::Tier2;
let json = serde_json::to_string(&tier).unwrap();
assert_eq!(json, "\"2\"");
let deserialized: ModerationTier = serde_json::from_str(&json).unwrap();
assert_eq!(tier, deserialized);
}
#[test]
fn message_encryption_wire_values() {
assert_eq!(
serde_json::to_string(&MessageEncryption::E2ee).unwrap(),
"\"e2ee\""
);
assert_eq!(
serde_json::to_string(&MessageEncryption::Server).unwrap(),
"\"server\""
);
assert_eq!(MessageEncryption::E2ee.to_string(), "e2ee");
assert_eq!(
MessageEncryption::from_str("server").unwrap(),
MessageEncryption::Server
);
}
#[test]
fn search_mode_wire_values() {
assert_eq!(
serde_json::to_string(&SearchMode::Keyword).unwrap(),
"\"keyword\""
);
assert_eq!(
serde_json::to_string(&SearchMode::Semantic).unwrap(),
"\"semantic\""
);
assert_eq!(
SearchMode::from_str("semantic").unwrap(),
SearchMode::Semantic
);
}
#[test]
fn no_sort_orders_by_votes() {
for gone in ["score", "controversial", "unpopular"] {
assert!(FeedSort::from_str(gone).is_err(), "{gone}");
assert!(
serde_json::from_str::<FeedSort>(&format!("\"{gone}\""))
.is_err(),
"{gone}"
);
}
assert!(ProposalSort::from_str("score").is_err());
}
#[cfg(feature = "schemars")]
#[test]
fn feed_sort_schema_is_ref_free() {
use schemars::JsonSchema;
assert!(<FeedSort as JsonSchema>::inline_schema());
let schema = schemars::schema_for!(FeedSort);
let value = serde_json::to_value(&schema).unwrap();
let blob = value.to_string();
assert!(value.get("$defs").is_none(), "no $defs: {value}");
assert!(!blob.contains("$ref"), "no $ref: {value}");
let values: Vec<&str> = value["enum"]
.as_array()
.expect("FeedSort should render as a flat `enum`")
.iter()
.filter_map(|v| v.as_str())
.collect();
assert_eq!(values, ["date", "active", "random", "diverse"], "{value}");
}
#[cfg(feature = "schemars")]
#[test]
fn trash_enum_schemas_are_plain_enums() {
fn values<T: schemars::JsonSchema>() -> Vec<String> {
assert!(T::inline_schema());
let value = serde_json::to_value(schemars::schema_for!(T)).unwrap();
let blob = value.to_string();
assert!(!blob.contains("$ref") && !blob.contains("$defs"));
assert!(value.get("oneOf").is_none(), "{value}");
value["enum"]
.as_array()
.unwrap_or_else(|| panic!("a flat `enum`: {value}"))
.iter()
.map(|v| v.as_str().unwrap().to_owned())
.collect()
}
assert_eq!(values::<DeletedBy>(), ["author", "operator"]);
assert_eq!(
values::<TrashMode>(),
["list", "restore", "delete_permanently"]
);
assert_eq!(values::<RemovedBy>(), ["author", "operator", "moderation"]);
assert_eq!(values::<ContentKind>(), ["post", "comment"]);
}
#[cfg(feature = "schemars")]
#[test]
fn obligation_enum_schemas_are_strict_safe() {
fn values<T: schemars::JsonSchema>() -> Vec<String> {
assert!(T::inline_schema());
let value = crate::responses::inline_schema_for::<T>();
assert_eq!(
crate::tools::strict_schema_violations(&value),
Vec::<String>::new()
);
value["enum"]
.as_array()
.unwrap_or_else(|| panic!("a flat `enum`: {value}"))
.iter()
.map(|v| v.as_str().unwrap().to_owned())
.collect()
}
assert_eq!(
values::<ObligationKind>(),
[
"carry_forward",
"ratification",
"review",
"referral",
"reveal",
"task"
]
);
assert_eq!(values::<OwedBy>(), ["steward", "staff", "council"]);
assert_eq!(
values::<ObligationStatus>(),
["open", "lapsed", "discharged", "withdrawn"]
);
assert_eq!(
values::<CalendarEventKind>(),
["sitting", "special_sitting"]
);
}
#[test]
fn trash_enum_wire_values() {
assert_eq!(DeletedBy::Operator.to_string(), "operator");
assert_eq!(
"delete_permanently".parse::<TrashMode>().unwrap(),
TrashMode::DeletePermanently
);
assert_eq!(TrashMode::default(), TrashMode::List);
assert_eq!(RemovedBy::from(DeletedBy::Author), RemovedBy::Author);
assert_eq!(ContentKind::Comment.to_string(), "comment");
}
#[test]
fn unknown_trash_causes_parse() {
assert_eq!(
serde_json::from_str::<RemovedBy>(r#""council""#).unwrap(),
RemovedBy::Unknown
);
assert_eq!(RemovedBy::Unknown.placeholder(), "[removed]");
assert_eq!(
serde_json::from_str::<DeletedBy>(r#""council""#).unwrap(),
DeletedBy::Unknown
);
assert_eq!(RemovedBy::from(DeletedBy::Unknown), RemovedBy::Unknown);
}
#[test]
fn proposal_category_round_trip() {
for cat in [
ProposalCategory::Routine,
ProposalCategory::Policy,
ProposalCategory::Constitutional,
ProposalCategory::Emergency,
] {
let json = serde_json::to_string(&cat).unwrap();
let back: ProposalCategory = serde_json::from_str(&json).unwrap();
assert_eq!(cat, back);
}
}
#[test]
fn governance_amendment_and_key_wire_values() {
assert_eq!(GovernanceLogEntryType::Amendment.to_string(), "amendment");
assert_eq!(
GovernanceLogEntryType::KeyRotation.to_string(),
"key_rotation"
);
assert_eq!(
AmendmentKind::NonPrecedential.to_string(),
"non_precedential"
);
assert_eq!(AmendmentKind::Reattested.to_string(), "reattested");
assert_eq!(AmendmentKind::Revision.to_string(), "revision");
assert_eq!(RecordVersion::default(), RecordVersion::Latest);
assert_eq!(RecordVersion::Original.to_string(), "original");
assert_eq!(
"latest".parse::<RecordVersion>().unwrap(),
RecordVersion::Latest
);
assert_eq!(
"superseded".parse::<AmendmentKind>().unwrap(),
AmendmentKind::Superseded
);
assert_eq!(Standing::default(), Standing::InForce);
assert_eq!(Standing::InForce.to_string(), "in_force");
assert_eq!(
"compromised".parse::<KeyStatus>().unwrap(),
KeyStatus::Compromised
);
assert_eq!(KeyStatus::Retired.to_string(), "retired");
}
#[test]
fn obligation_and_calendar_wire_values() {
assert_eq!(
GovernanceLogEntryType::Obligation.to_string(),
"obligation"
);
assert_eq!(GovernanceLogEntryType::DataSet.to_string(), "data_set");
assert_eq!(
"data_set".parse::<GovernanceLogEntryType>().unwrap(),
GovernanceLogEntryType::DataSet
);
assert_eq!(GovernanceLogEntryType::Test.to_string(), "test");
assert_eq!(
"test".parse::<GovernanceLogEntryType>().unwrap(),
GovernanceLogEntryType::Test
);
assert_eq!(
serde_json::from_value::<GovernanceLogEntryType>(
serde_json::json!("something_newer")
)
.unwrap(),
GovernanceLogEntryType::Unknown
);
assert!(
!GovernanceLogEntryType::ALL
.contains(&GovernanceLogEntryType::Unknown)
);
let kinds: Vec<String> =
AmendmentKind::OBLIGATION.map(|k| k.to_string()).to_vec();
assert_eq!(
kinds,
[
"planned",
"discharged",
"lapsed",
"rescheduled",
"withdrawn"
]
);
assert!(AmendmentKind::Lapsed.is_obligation());
assert!(!AmendmentKind::Revision.is_obligation());
for (kind, wire) in [
(ObligationKind::CarryForward, "carry_forward"),
(ObligationKind::Ratification, "ratification"),
(ObligationKind::Review, "review"),
(ObligationKind::Referral, "referral"),
(ObligationKind::Reveal, "reveal"),
(ObligationKind::Task, "task"),
] {
assert_eq!(kind.to_string(), wire);
assert_eq!(wire.parse::<ObligationKind>().unwrap(), kind);
}
for (who, wire) in [
(OwedBy::Steward, "steward"),
(OwedBy::Staff, "staff"),
(OwedBy::Council, "council"),
] {
assert_eq!(who.to_string(), wire);
assert_eq!(wire.parse::<OwedBy>().unwrap(), who);
}
assert_eq!(ObligationStatus::default(), ObligationStatus::Open);
assert_eq!(ObligationStatus::Lapsed.to_string(), "lapsed");
assert!(ObligationStatus::Withdrawn.is_closed());
assert!(!ObligationStatus::Lapsed.is_closed());
assert_eq!(
CalendarEventKind::SpecialSitting.to_string(),
"special_sitting"
);
assert!("festival".parse::<CalendarEventKind>().is_err());
assert_eq!(
"cancelled".parse::<CalendarEventStatus>().unwrap(),
CalendarEventStatus::Cancelled
);
}
#[test]
fn detail_level_wire_names() {
for (level, wire) in [
(DetailLevel::Summary, "summary"),
(DetailLevel::Full, "full"),
(DetailLevel::FullWithAttachments, "full_with_attachments"),
] {
assert_eq!(serde_json::to_value(level).unwrap(), wire);
assert_eq!(level.to_string(), wire);
assert_eq!(wire.parse::<DetailLevel>().unwrap(), level);
}
}
#[cfg(feature = "schemars")]
#[test]
fn enum_json_schema_is_inlined() {
use schemars::JsonSchema;
assert!(<TargetType as JsonSchema>::inline_schema());
assert!(<FeedSort as JsonSchema>::inline_schema());
assert!(<DashboardCommunities as JsonSchema>::inline_schema());
assert!(<ProposalSort as JsonSchema>::inline_schema());
assert!(<DetailLevel as JsonSchema>::inline_schema());
assert!(<RecordVersion as JsonSchema>::inline_schema());
assert!(<SearchMode as JsonSchema>::inline_schema());
assert!(<ProposalCategory as JsonSchema>::inline_schema());
assert!(<GovernanceLogEntryType as JsonSchema>::inline_schema());
assert!(<AmendmentKind as JsonSchema>::inline_schema());
assert!(<Standing as JsonSchema>::inline_schema());
assert!(<CitationRelation as JsonSchema>::inline_schema());
assert!(<KeyStatus as JsonSchema>::inline_schema());
assert!(<OAuthScope as JsonSchema>::inline_schema());
assert!(<ModerationTargetType as JsonSchema>::inline_schema());
assert!(<ModerationTier as JsonSchema>::inline_schema());
assert!(<ObligationKind as JsonSchema>::inline_schema());
assert!(<OwedBy as JsonSchema>::inline_schema());
assert!(<ObligationStatus as JsonSchema>::inline_schema());
assert!(<CalendarEventKind as JsonSchema>::inline_schema());
assert!(<CalendarEventStatus as JsonSchema>::inline_schema());
#[derive(schemars::JsonSchema)]
#[allow(dead_code)]
struct Container {
target_type: TargetType,
sort: Option<FeedSort>,
proposal_sort: Option<ProposalSort>,
category: Option<ProposalCategory>,
detail: Option<DetailLevel>,
version: Option<RecordVersion>,
search_mode: Option<SearchMode>,
}
let schema = schemars::schema_for!(Container);
let value = serde_json::to_value(&schema).unwrap();
let blob = value.to_string();
assert!(
value.get("$defs").is_none(),
"no $defs should be emitted for enum-only container; got schema: {value}"
);
assert!(
!blob.contains("$ref"),
"enum container schema must contain no $ref anywhere; got: {value}"
);
let target_type_enum = value["properties"]["target_type"]["enum"]
.as_array()
.expect("target_type should have inline `enum` array");
assert!(
target_type_enum
.contains(&serde_json::Value::String("post".into()))
);
assert!(
target_type_enum
.contains(&serde_json::Value::String("comment".into()))
);
}
#[cfg(feature = "schemars")]
#[test]
fn search_mode_schema_is_ref_free() {
use schemars::JsonSchema;
assert!(<SearchMode as JsonSchema>::inline_schema());
let schema = schemars::schema_for!(SearchMode);
let value = serde_json::to_value(&schema).unwrap();
let blob = value.to_string();
assert!(value.get("$defs").is_none(), "no $defs: {value}");
assert!(!blob.contains("$ref"), "no $ref: {value}");
let variants = value["oneOf"]
.as_array()
.expect("SearchMode should have an inline `oneOf` array");
let consts: Vec<&str> = variants
.iter()
.filter_map(|v| v["const"].as_str())
.collect();
assert!(consts.contains(&"keyword"), "{value}");
assert!(consts.contains(&"semantic"), "{value}");
}
}