Skip to main content

runx_contracts/
reference.rs

1//! Reference contracts: typed references to receipts, acts, and external surfaces.
2use serde::{Deserialize, Serialize};
3
4use crate::schema::{IsoDateTime, NonEmptyString, RunxSchema};
5
6#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
7#[serde(rename_all = "snake_case")]
8pub enum ReferenceType {
9    GithubIssue,
10    GithubPullRequest,
11    GithubRepo,
12    SlackThread,
13    SentryEvent,
14    ProviderThread,
15    ProviderEvent,
16    ProviderComment,
17    TrackingItem,
18    ChangeRequest,
19    Repository,
20    SupportTicket,
21    Signal,
22    Act,
23    Receipt,
24    GraphReceipt,
25    Artifact,
26    Verification,
27    Harness,
28    Host,
29    Deployment,
30    Surface,
31    Target,
32    Opportunity,
33    ThesisAssessment,
34    Selection,
35    SkillBinding,
36    TargetTransitionEntry,
37    SelectionCycle,
38    Decision,
39    ReflectionEntry,
40    FeedEntry,
41    Principal,
42    AuthorityProof,
43    ScopeAdmission,
44    Grant,
45    Mandate,
46    Credential,
47    WebhookDelivery,
48    RedactionPolicy,
49    ExternalUrl,
50}
51
52impl ReferenceType {
53    /// Stable snake_case wire name for this reference type. Matches the serde
54    /// representation and the `runx:<name>:<id>` URI segment.
55    pub fn as_str(&self) -> &'static str {
56        match self {
57            ReferenceType::GithubIssue => "github_issue",
58            ReferenceType::GithubPullRequest => "github_pull_request",
59            ReferenceType::GithubRepo => "github_repo",
60            ReferenceType::SlackThread => "slack_thread",
61            ReferenceType::SentryEvent => "sentry_event",
62            ReferenceType::ProviderThread => "provider_thread",
63            ReferenceType::ProviderEvent => "provider_event",
64            ReferenceType::ProviderComment => "provider_comment",
65            ReferenceType::TrackingItem => "tracking_item",
66            ReferenceType::ChangeRequest => "change_request",
67            ReferenceType::Repository => "repository",
68            ReferenceType::SupportTicket => "support_ticket",
69            ReferenceType::Signal => "signal",
70            ReferenceType::Act => "act",
71            ReferenceType::Receipt => "receipt",
72            ReferenceType::GraphReceipt => "graph_receipt",
73            ReferenceType::Artifact => "artifact",
74            ReferenceType::Verification => "verification",
75            ReferenceType::Harness => "harness",
76            ReferenceType::Host => "host",
77            ReferenceType::Deployment => "deployment",
78            ReferenceType::Surface => "surface",
79            ReferenceType::Target => "target",
80            ReferenceType::Opportunity => "opportunity",
81            ReferenceType::ThesisAssessment => "thesis_assessment",
82            ReferenceType::Selection => "selection",
83            ReferenceType::SkillBinding => "skill_binding",
84            ReferenceType::TargetTransitionEntry => "target_transition_entry",
85            ReferenceType::SelectionCycle => "selection_cycle",
86            ReferenceType::Decision => "decision",
87            ReferenceType::ReflectionEntry => "reflection_entry",
88            ReferenceType::FeedEntry => "feed_entry",
89            ReferenceType::Principal => "principal",
90            ReferenceType::AuthorityProof => "authority_proof",
91            ReferenceType::ScopeAdmission => "scope_admission",
92            ReferenceType::Grant => "grant",
93            ReferenceType::Mandate => "mandate",
94            ReferenceType::Credential => "credential",
95            ReferenceType::WebhookDelivery => "webhook_delivery",
96            ReferenceType::RedactionPolicy => "redaction_policy",
97            ReferenceType::ExternalUrl => "external_url",
98        }
99    }
100}
101
102#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
103#[serde(rename_all = "snake_case")]
104pub enum ProofKind {
105    EffectEvidence,
106    EffectFinality,
107    CredentialResolution,
108}
109
110#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
111#[serde(deny_unknown_fields)]
112#[runx_schema(id = "runx.reference.v1")]
113pub struct Reference {
114    #[serde(rename = "type")]
115    pub reference_type: ReferenceType,
116    pub uri: NonEmptyString,
117    #[serde(skip_serializing_if = "Option::is_none")]
118    pub provider: Option<NonEmptyString>,
119    #[serde(skip_serializing_if = "Option::is_none")]
120    pub locator: Option<NonEmptyString>,
121    #[serde(skip_serializing_if = "Option::is_none")]
122    pub label: Option<NonEmptyString>,
123    #[serde(skip_serializing_if = "Option::is_none")]
124    pub observed_at: Option<IsoDateTime>,
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub proof_kind: Option<ProofKind>,
127}
128
129impl Reference {
130    /// A reference to an explicit URI, with no provider/locator/label/proof.
131    pub fn with_uri(reference_type: ReferenceType, uri: impl Into<NonEmptyString>) -> Self {
132        Self {
133            reference_type,
134            uri: uri.into(),
135            provider: None,
136            locator: None,
137            label: None,
138            observed_at: None,
139            proof_kind: None,
140        }
141    }
142
143    /// A reference whose URI is the canonical `runx:<type>:<id>` scheme.
144    pub fn runx(reference_type: ReferenceType, id: &str) -> Self {
145        let uri = format!("runx:{}:{id}", reference_type.as_str());
146        Self::with_uri(reference_type, uri)
147    }
148}
149
150#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
151#[serde(deny_unknown_fields)]
152#[runx_schema(id = "runx.reference_link.v1")]
153pub struct ReferenceLink {
154    pub role: NonEmptyString,
155    #[serde(rename = "ref")]
156    pub reference: Reference,
157}
158
159#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, RunxSchema)]
160#[serde(deny_unknown_fields)]
161pub struct ActRef {
162    pub receipt_ref: Reference,
163    pub act_id: NonEmptyString,
164}