Skip to main content

adk_payments/domain/
evidence.rs

1use serde::{Deserialize, Serialize};
2use serde_json::{Map, Value};
3
4/// Identifies the protocol and version that originated canonical data.
5#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
6#[serde(rename_all = "camelCase")]
7pub struct ProtocolDescriptor {
8    pub name: String,
9    #[serde(default, skip_serializing_if = "Option::is_none")]
10    pub version: Option<String>,
11}
12
13impl ProtocolDescriptor {
14    /// Creates a protocol descriptor for ACP.
15    #[must_use]
16    pub fn acp(version: impl Into<String>) -> Self {
17        Self { name: "acp".to_string(), version: Some(version.into()) }
18    }
19
20    /// Creates a protocol descriptor for AP2.
21    #[must_use]
22    pub fn ap2(version: impl Into<String>) -> Self {
23        Self { name: "ap2".to_string(), version: Some(version.into()) }
24    }
25
26    /// Creates a protocol descriptor for any named protocol.
27    #[must_use]
28    pub fn new(name: impl Into<String>, version: Option<String>) -> Self {
29        Self { name: name.into(), version }
30    }
31}
32
33/// Opaque reference to a stored evidence artifact.
34#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
35#[serde(rename_all = "camelCase")]
36pub struct EvidenceReference {
37    pub evidence_id: String,
38    pub protocol: ProtocolDescriptor,
39    pub artifact_kind: String,
40    #[serde(default, skip_serializing_if = "Option::is_none")]
41    pub digest: Option<String>,
42}
43
44/// Lossless protocol-specific fields preserved alongside canonical state.
45#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
46#[serde(rename_all = "camelCase")]
47pub struct ProtocolExtensionEnvelope {
48    pub protocol: ProtocolDescriptor,
49    #[serde(default, skip_serializing_if = "Map::is_empty")]
50    pub fields: Map<String, Value>,
51    #[serde(default, skip_serializing_if = "Vec::is_empty")]
52    pub evidence_refs: Vec<EvidenceReference>,
53}
54
55impl ProtocolExtensionEnvelope {
56    /// Creates an empty extension envelope for one protocol source.
57    #[must_use]
58    pub fn new(protocol: ProtocolDescriptor) -> Self {
59        Self { protocol, fields: Map::new(), evidence_refs: Vec::new() }
60    }
61
62    /// Appends one raw protocol field without discarding its original shape.
63    #[must_use]
64    pub fn with_field(mut self, key: impl Into<String>, value: Value) -> Self {
65        self.fields.insert(key.into(), value);
66        self
67    }
68
69    /// Appends one evidence reference associated with the extension envelope.
70    #[must_use]
71    pub fn with_evidence_ref(mut self, evidence_ref: EvidenceReference) -> Self {
72        self.evidence_refs.push(evidence_ref);
73        self
74    }
75
76    /// Returns `true` when the envelope carries neither fields nor evidence
77    /// references.
78    #[must_use]
79    pub fn is_empty(&self) -> bool {
80        self.fields.is_empty() && self.evidence_refs.is_empty()
81    }
82}
83
84/// Collection of preserved protocol extension envelopes.
85#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
86#[serde(transparent)]
87pub struct ProtocolExtensions(pub Vec<ProtocolExtensionEnvelope>);
88
89impl ProtocolExtensions {
90    /// Returns `true` when no protocol-specific envelopes are attached.
91    #[must_use]
92    pub fn is_empty(&self) -> bool {
93        self.0.is_empty()
94    }
95
96    /// Adds one protocol envelope to the collection.
97    pub fn push(&mut self, envelope: ProtocolExtensionEnvelope) {
98        self.0.push(envelope);
99    }
100
101    /// Returns all attached envelopes.
102    #[must_use]
103    pub fn as_slice(&self) -> &[ProtocolExtensionEnvelope] {
104        &self.0
105    }
106}
107
108impl From<Vec<ProtocolExtensionEnvelope>> for ProtocolExtensions {
109    fn from(value: Vec<ProtocolExtensionEnvelope>) -> Self {
110        Self(value)
111    }
112}