Skip to main content

higher_graphen_core/extension/
common.rs

1use crate::text::normalize_required_text;
2use crate::{Id, Result};
3use serde::{Deserialize, Serialize};
4
5/// Reference to a HigherGraphen object encoded by its stable identifier.
6#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
7#[serde(deny_unknown_fields)]
8pub struct ObjectRef {
9    /// Target object reference, such as `cell:customer` or `derivation:proof`.
10    #[serde(rename = "ref")]
11    pub reference: Id,
12}
13
14impl ObjectRef {
15    /// Creates a reference from a stable object identifier.
16    pub fn new(reference: Id) -> Self {
17        Self { reference }
18    }
19}
20
21/// Free-text structural note retained in machine-readable objects.
22#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
23#[serde(deny_unknown_fields)]
24pub struct Description {
25    /// Human-readable description.
26    pub description: String,
27}
28
29impl Description {
30    /// Creates a validated description.
31    pub fn new(description: impl Into<String>) -> Result<Self> {
32        Ok(Self {
33            description: normalize_required_text("description", description)?,
34        })
35    }
36}
37
38/// Candidate lifecycle shared by reviewable core extension objects.
39#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
40#[serde(rename_all = "snake_case")]
41pub enum LifecycleStatus {
42    /// Proposed by an engine or actor, not accepted fact.
43    Candidate,
44    /// Awaiting or undergoing explicit review.
45    UnderReview,
46    /// Explicitly accepted for its declared scope.
47    Accepted,
48    /// Explicitly rejected.
49    Rejected,
50    /// Replaced by a later object.
51    Superseded,
52}
53
54impl LifecycleStatus {
55    /// Returns true when the object is accepted for its declared scope.
56    pub fn is_accepted(self) -> bool {
57        matches!(self, Self::Accepted)
58    }
59}
60
61/// Explicit review requirement for a promotion or operation.
62#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
63#[serde(deny_unknown_fields)]
64pub struct ReviewRequirement {
65    /// Whether explicit review is required.
66    pub required: bool,
67    /// Reviewer identity when a specific reviewer is required.
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub reviewer: Option<Id>,
70    /// Review decision or requirement rationale.
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub decision_reason: Option<String>,
73}
74
75impl ReviewRequirement {
76    /// Creates a review requirement.
77    pub fn new(required: bool) -> Self {
78        Self {
79            required,
80            reviewer: None,
81            decision_reason: None,
82        }
83    }
84
85    /// Returns this requirement with a validated decision reason.
86    pub fn with_decision_reason(mut self, decision_reason: impl Into<String>) -> Result<Self> {
87        self.decision_reason = Some(normalize_required_text("decision_reason", decision_reason)?);
88        Ok(self)
89    }
90}