Skip to main content

uor_addr/ring/
value.rs

1//! `RingElement` — the ring-element typed input handle (UOR-Framework
2//! Amendment 43 §2 `Element::canonical_bytes`).
3//!
4//! Runtime bytes are
5//!
6//! ```text
7//! canonical_bytes(e) := [witt_level: u8] || [coefficient: u8; witt_level + 1]
8//! ```
9//!
10//! which is already the canonical form, so under ADR-060 the handle's
11//! `as_binding_value` returns the tagged bytes directly as an `Inline`
12//! [`TermValue`] carrier (the form is ≤ 5 bytes, well within the
13//! foundation-derived inline width). ψ₉ folds that carrier through the
14//! σ-axis to mint the κ-label.
15
16use prism::operation::TermValue;
17use prism::pipeline::{
18    ConstrainedTypeShape, ConstraintRef, IntoBindingValue, PartitionProductFields, ShapeViolation,
19    ViolationKind,
20};
21
22use crate::ring::shapes::bounds::{MAX_WITT_LEVEL, RING_VALUE_MAX_BYTES};
23
24// ─── ShapeViolation IRIs ────────────────────────────────────────────────
25
26const INVALID_RING_VIOLATION: ShapeViolation = ShapeViolation {
27    shape_iri: "https://uor.foundation/addr/RingElement",
28    constraint_iri: "https://uor.foundation/addr/RingElement/validCanonicalBytes",
29    property_iri: "https://uor.foundation/addr/inputBytes",
30    expected_range: "https://uor.foundation/addr/ValidRingElementBytes",
31    min_count: 0,
32    max_count: 1,
33    kind: ViolationKind::ValueCheck,
34};
35
36const WITT_LEVEL_VIOLATION: ShapeViolation = ShapeViolation {
37    shape_iri: "https://uor.foundation/addr/RingElement",
38    constraint_iri: "https://uor.foundation/addr/RingElement/wittLevelBound",
39    property_iri: "https://uor.foundation/addr/RingElement/wittLevel",
40    expected_range: "http://www.w3.org/2001/XMLSchema#unsignedByte",
41    min_count: 0,
42    max_count: MAX_WITT_LEVEL as u32,
43    kind: ViolationKind::CardinalityViolation,
44};
45
46const TOTAL_WIDTH_VIOLATION: ShapeViolation = ShapeViolation {
47    shape_iri: "https://uor.foundation/addr/RingElement",
48    constraint_iri: "https://uor.foundation/addr/RingElement/serializedWidth",
49    property_iri: "https://uor.foundation/addr/RingElement/totalByteCount",
50    expected_range: "http://www.w3.org/2001/XMLSchema#nonNegativeInteger",
51    min_count: 0,
52    max_count: RING_VALUE_MAX_BYTES as u32,
53    kind: ViolationKind::CardinalityViolation,
54};
55
56// ─── RingElement — the typed input handle ────────────────────────────────
57
58/// Typed ring-element input handle. Runtime bytes follow Amendment 43
59/// §2's canonical-bytes layout, stored in a fixed-size stack buffer.
60#[derive(Clone)]
61pub struct RingElement {
62    pub(crate) bytes: [u8; RING_VALUE_MAX_BYTES],
63    pub(crate) len: u16,
64}
65
66impl core::fmt::Debug for RingElement {
67    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
68        f.debug_struct("RingElement")
69            .field("len", &self.len)
70            .finish_non_exhaustive()
71    }
72}
73
74impl PartialEq for RingElement {
75    fn eq(&self, other: &Self) -> bool {
76        self.tagged_bytes() == other.tagged_bytes()
77    }
78}
79impl Eq for RingElement {}
80
81impl RingElement {
82    /// Construct a `RingElement` from explicit Witt level + coefficient.
83    pub fn from_components(witt_level: u8, coefficient: u64) -> Result<Self, ShapeViolation> {
84        if witt_level > MAX_WITT_LEVEL {
85            return Err(WITT_LEVEL_VIOLATION);
86        }
87        let coefficient_bytes = (witt_level + 1) as usize;
88        let total = 1 + coefficient_bytes;
89        let mut me = Self {
90            bytes: [0u8; RING_VALUE_MAX_BYTES],
91            len: total as u16,
92        };
93        me.bytes[0] = witt_level;
94        let le = coefficient.to_le_bytes();
95        me.bytes[1..1 + coefficient_bytes].copy_from_slice(&le[..coefficient_bytes]);
96        Ok(me)
97    }
98
99    /// Parse raw canonical-bytes into a typed `RingElement`.
100    pub fn parse(raw: &[u8]) -> Result<Self, ShapeViolation> {
101        if raw.is_empty() {
102            return Err(INVALID_RING_VIOLATION);
103        }
104        if raw.len() > RING_VALUE_MAX_BYTES {
105            return Err(TOTAL_WIDTH_VIOLATION);
106        }
107        let witt_level = raw[0];
108        if witt_level > MAX_WITT_LEVEL {
109            return Err(WITT_LEVEL_VIOLATION);
110        }
111        let expected_len = 1 + (witt_level as usize + 1);
112        if raw.len() != expected_len {
113            return Err(INVALID_RING_VIOLATION);
114        }
115        let mut me = Self {
116            bytes: [0u8; RING_VALUE_MAX_BYTES],
117            len: raw.len() as u16,
118        };
119        me.bytes[..raw.len()].copy_from_slice(raw);
120        Ok(me)
121    }
122
123    /// Borrow the canonical-bytes byte sequence.
124    #[must_use]
125    pub fn tagged_bytes(&self) -> &[u8] {
126        &self.bytes[..self.len as usize]
127    }
128
129    /// The element's Witt level (first byte of the canonical layout).
130    #[must_use]
131    pub fn witt_level(&self) -> u8 {
132        self.bytes[0]
133    }
134}
135
136// ─── ConstrainedTypeShape + IntoBindingValue + PartitionProductFields ─────
137
138impl ConstrainedTypeShape for RingElement {
139    const IRI: &'static str = "https://uor.foundation/addr/RingElement";
140    const SITE_COUNT: usize = 1;
141    const CONSTRAINTS: &'static [ConstraintRef] = &[];
142    const CYCLE_SIZE: u64 = u64::MAX;
143}
144
145impl prism::uor_foundation::pipeline::__sdk_seal::Sealed for RingElement {}
146
147impl<'a> IntoBindingValue<'a> for RingElement {
148    fn as_binding_value<const INLINE_BYTES: usize>(&self) -> TermValue<'a, INLINE_BYTES> {
149        // Amendment 43 §2 canonical bytes are the canonical form; emit them
150        // as an `Inline` carrier (owned, valid for any `'a`).
151        TermValue::inline_from_slice(self.tagged_bytes())
152    }
153}
154
155impl PartitionProductFields for RingElement {
156    const FIELDS: &'static [(u32, u32)] = &[];
157    const FIELD_NAMES: &'static [&'static str] = &[];
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163
164    #[test]
165    fn from_components_round_trip() {
166        let e = RingElement::from_components(2, 0x0001_0203).expect("valid");
167        assert_eq!(e.bytes[0], 2);
168        assert_eq!(&e.bytes[1..4], &[0x03, 0x02, 0x01]);
169    }
170
171    #[test]
172    fn parse_matches_construction() {
173        let constructed = RingElement::from_components(1, 0x0102).expect("valid");
174        let parsed = RingElement::parse(&[1, 0x02, 0x01]).expect("valid");
175        assert_eq!(constructed, parsed);
176    }
177
178    #[test]
179    fn rejects_overflow_witt_level() {
180        let err = RingElement::from_components(MAX_WITT_LEVEL + 1, 0).expect_err("must reject");
181        assert_eq!(err.constraint_iri, WITT_LEVEL_VIOLATION.constraint_iri);
182        let err = RingElement::parse(&[MAX_WITT_LEVEL + 1, 0]).expect_err("must reject");
183        assert_eq!(err.constraint_iri, WITT_LEVEL_VIOLATION.constraint_iri);
184    }
185
186    #[test]
187    fn rejects_truncated_bytes() {
188        let err = RingElement::parse(&[2, 0, 0]).expect_err("must reject");
189        assert_eq!(err.constraint_iri, INVALID_RING_VIOLATION.constraint_iri);
190    }
191}