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,
}
#[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,
}
#[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,
}
#[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,
}
#[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,
}
#[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,
}
#[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,
Score,
Active,
Random,
Controversial,
Diverse,
}
#[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,
}
impl_display_fromstr!(TargetType);
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!(ProposalCategory);
impl_display_fromstr!(GovernanceLogEntryType);
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!(FriendshipStatus);
impl_display_fromstr!(FriendshipAction);
impl_display_fromstr!(BlockAction);
impl_display_fromstr!(MessageEncryption);
#[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 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);
}
}
#[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!(<ProposalCategory as JsonSchema>::inline_schema());
assert!(<GovernanceLogEntryType as JsonSchema>::inline_schema());
assert!(<OAuthScope as JsonSchema>::inline_schema());
assert!(<ModerationTargetType as JsonSchema>::inline_schema());
assert!(<ModerationTier as JsonSchema>::inline_schema());
#[derive(schemars::JsonSchema)]
#[allow(dead_code)]
struct Container {
target_type: TargetType,
sort: Option<FeedSort>,
category: Option<ProposalCategory>,
}
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()))
);
}
}