idiolect-records 0.8.0

Rust record types mirroring the dev.idiolect.* Lexicon family.
Documentation
// @generated by idiolect-codegen. do not edit.
// source: dev.idiolect.deliberation

//! A community-scoped question or proposal under collective consideration. Companion records `dev.idiolect.deliberationStatement` carry participant utterances, `dev.idiolect.deliberationVote` carries stances on those statements, and `dev.idiolect.deliberationOutcome` carries the observer-aggregated tally. Deliberations are intentionally process-shaped: they represent the *unsettled* moment, distinct from `dev.idiolect.belief` (settled doxastic) and `dev.idiolect.recommendation` (settled normative). A deliberation that closes can name an outcome record so consumers can read the conclusion without re-folding the votes.

#![allow(
    missing_docs,
    clippy::doc_markdown,
    clippy::struct_excessive_bools,
    clippy::derive_partial_eq_without_eq,
    clippy::large_enum_variant
)]
use serde::{Deserialize, Serialize};

/// A deliberation: a topic or question owned by a community, optionally bracketed by a status and classification. Topic and description carry the human framing; classification and status are open-enum slugs resolved through community-published vocabularies, so a community can decline to take a stance on those axes simply by omitting them.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Deliberation {
    /// Whether participation requires the participant be an authenticated member of `owningCommunity`. False permits drive-by statements.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub auth_required: Option<bool>,
    /// Open-enum slug naming the deliberation's shape. Resolved against `classificationVocab` when present, otherwise against the canonical idiolect vocabulary.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub classification: Option<DeliberationClassification>,
    /// Vocabulary the `classification` slug resolves against. Omit to use the canonical idiolect default.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub classification_vocab: Option<crate::generated::dev::idiolect::defs::VocabRef>,
    /// When the deliberation moved out of an open status. Optional; absent means open or never explicitly closed.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub closed_at: Option<idiolect_records::Datetime>,
    pub created_at: idiolect_records::Datetime,
    /// Extended framing or context for the deliberation.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    /// AT-URI of a `dev.idiolect.deliberationOutcome` record summarising the resolved stance. Set after closure when an outcome exists; consumers can read it without re-folding the vote stream.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub outcome: Option<idiolect_records::AtUri>,
    /// AT-URI of the `dev.idiolect.community` record whose membership is deliberating. Consumers resolve membership/permissions through that record.
    pub owning_community: idiolect_records::AtUri,
    /// Open-enum lifecycle marker. Resolved against `statusVocab` when present.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub status: Option<DeliberationStatus>,
    /// Vocabulary the `status` slug resolves against.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub status_vocab: Option<crate::generated::dev::idiolect::defs::VocabRef>,
    /// Human-readable topic or question.
    pub topic: String,
}

impl crate::Record for Deliberation {
    const NSID: &'static str = "dev.idiolect.deliberation";
}

/// DeliberationClassification. Open-enum slug; known values are kebab-cased; community-extended values pass through as `Other(String)`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum DeliberationClassification {
    Question,
    Proposal,
    Grievance,
    Retrospective,
    /// Community-extended slug not present in the lexicon's
    /// `knownValues`. Resolves through the sibling
    /// `*Vocab` field on the containing record.
    Other(String),
}
impl DeliberationClassification {
    /// Wire-form slug for this value. Known variants render
    /// kebab-case; the fallback variant passes through verbatim.
    #[must_use]
    pub fn as_str(&self) -> &str {
        match self {
            Self::Question => "question",
            Self::Proposal => "proposal",
            Self::Grievance => "grievance",
            Self::Retrospective => "retrospective",
            Self::Other(s) => s.as_str(),
        }
    }
    /// Whether this slug is subsumed by `ancestor` under the
    /// `subsumed_by` relation in the supplied vocab. Reflexive:
    /// every slug is subsumed by itself.
    #[must_use]
    pub fn is_subsumed_by(
        &self,
        vocab: &idiolect_records::vocab::VocabGraph,
        ancestor: &str,
    ) -> bool {
        vocab.is_subsumed_by(self.as_str(), ancestor)
    }
    /// Whether this slug satisfies a requirement of `target`
    /// under the named `relation` in the supplied vocab.
    /// Generalises `is_subsumed_by` to any directed relation
    /// (e.g. `stronger_than`, `provides_at_least`,
    /// `equivalent_to`). Reflexive: a slug satisfies itself.
    #[must_use]
    pub fn satisfies(
        &self,
        vocab: &idiolect_records::vocab::VocabGraph,
        relation: &str,
        target: &str,
    ) -> bool {
        if self.as_str() == target {
            return true;
        }
        vocab
            .walk_relation(self.as_str(), relation, false)
            .iter()
            .any(|n| n == target)
    }
    /// Translate this slug across vocabularies via
    /// `equivalent_to` edges. Returns the translated slug as
    /// a target enum value when a translation exists, `None`
    /// when no path is found (callers fall back to passing
    /// the slug through verbatim, which is wire-compatible).
    #[must_use]
    pub fn translate_to<T: From<String>>(
        &self,
        src_vocab_uri: &str,
        tgt_vocab_uri: &str,
        registry: &idiolect_records::vocab::VocabRegistry,
    ) -> Option<T> {
        registry
            .translate(src_vocab_uri, tgt_vocab_uri, self.as_str())
            .map(T::from)
    }
}
impl From<String> for DeliberationClassification {
    fn from(s: String) -> Self {
        match s.as_str() {
            "question" => Self::Question,
            "proposal" => Self::Proposal,
            "grievance" => Self::Grievance,
            "retrospective" => Self::Retrospective,
            _ => Self::Other(s),
        }
    }
}
impl From<&str> for DeliberationClassification {
    fn from(s: &str) -> Self {
        match s {
            "question" => Self::Question,
            "proposal" => Self::Proposal,
            "grievance" => Self::Grievance,
            "retrospective" => Self::Retrospective,
            _ => Self::Other(s.to_owned()),
        }
    }
}
impl serde::Serialize for DeliberationClassification {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.serialize_str(self.as_str())
    }
}
impl<'de> serde::Deserialize<'de> for DeliberationClassification {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        Ok(Self::from(s))
    }
}

/// DeliberationStatus. Open-enum slug; known values are kebab-cased; community-extended values pass through as `Other(String)`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum DeliberationStatus {
    Open,
    Closed,
    Tabled,
    Adopted,
    Rejected,
    /// Community-extended slug not present in the lexicon's
    /// `knownValues`. Resolves through the sibling
    /// `*Vocab` field on the containing record.
    Other(String),
}
impl DeliberationStatus {
    /// Wire-form slug for this value. Known variants render
    /// kebab-case; the fallback variant passes through verbatim.
    #[must_use]
    pub fn as_str(&self) -> &str {
        match self {
            Self::Open => "open",
            Self::Closed => "closed",
            Self::Tabled => "tabled",
            Self::Adopted => "adopted",
            Self::Rejected => "rejected",
            Self::Other(s) => s.as_str(),
        }
    }
    /// Whether this slug is subsumed by `ancestor` under the
    /// `subsumed_by` relation in the supplied vocab. Reflexive:
    /// every slug is subsumed by itself.
    #[must_use]
    pub fn is_subsumed_by(
        &self,
        vocab: &idiolect_records::vocab::VocabGraph,
        ancestor: &str,
    ) -> bool {
        vocab.is_subsumed_by(self.as_str(), ancestor)
    }
    /// Whether this slug satisfies a requirement of `target`
    /// under the named `relation` in the supplied vocab.
    /// Generalises `is_subsumed_by` to any directed relation
    /// (e.g. `stronger_than`, `provides_at_least`,
    /// `equivalent_to`). Reflexive: a slug satisfies itself.
    #[must_use]
    pub fn satisfies(
        &self,
        vocab: &idiolect_records::vocab::VocabGraph,
        relation: &str,
        target: &str,
    ) -> bool {
        if self.as_str() == target {
            return true;
        }
        vocab
            .walk_relation(self.as_str(), relation, false)
            .iter()
            .any(|n| n == target)
    }
    /// Translate this slug across vocabularies via
    /// `equivalent_to` edges. Returns the translated slug as
    /// a target enum value when a translation exists, `None`
    /// when no path is found (callers fall back to passing
    /// the slug through verbatim, which is wire-compatible).
    #[must_use]
    pub fn translate_to<T: From<String>>(
        &self,
        src_vocab_uri: &str,
        tgt_vocab_uri: &str,
        registry: &idiolect_records::vocab::VocabRegistry,
    ) -> Option<T> {
        registry
            .translate(src_vocab_uri, tgt_vocab_uri, self.as_str())
            .map(T::from)
    }
}
impl From<String> for DeliberationStatus {
    fn from(s: String) -> Self {
        match s.as_str() {
            "open" => Self::Open,
            "closed" => Self::Closed,
            "tabled" => Self::Tabled,
            "adopted" => Self::Adopted,
            "rejected" => Self::Rejected,
            _ => Self::Other(s),
        }
    }
}
impl From<&str> for DeliberationStatus {
    fn from(s: &str) -> Self {
        match s {
            "open" => Self::Open,
            "closed" => Self::Closed,
            "tabled" => Self::Tabled,
            "adopted" => Self::Adopted,
            "rejected" => Self::Rejected,
            _ => Self::Other(s.to_owned()),
        }
    }
}
impl serde::Serialize for DeliberationStatus {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.serialize_str(self.as_str())
    }
}
impl<'de> serde::Deserialize<'de> for DeliberationStatus {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        Ok(Self::from(s))
    }
}