known_values/
known_value.rs

1use std::{
2    borrow::Cow,
3    fmt::{Display, Formatter},
4};
5
6use bc_components::{Digest, DigestProvider, tags};
7use dcbor::prelude::*;
8
9#[derive(Debug, Clone)]
10enum KnownValueName {
11    Static(&'static str),
12    Dynamic(String),
13}
14
15/// A value in a namespace of unsigned integers that represents a stand-alone
16/// ontological concept.
17///
18/// Known Values provide a compact, deterministic way to represent commonly used
19/// ontological concepts such as relationships between entities, classes of
20/// entities, properties, or enumerated values. They are particularly useful as
21/// predicates in Gordian Envelope assertions, offering a more compact and
22/// deterministic alternative to URIs. However, known values are not exclusive
23/// to Gordian Envelopes and can be used in any context where a compact, unique
24/// identifier for a concept is needed.
25///
26/// A Known Value is represented as a 64-bit unsigned integer with an optional
27/// human-readable name. This approach ensures:
28///
29/// - **Compact binary representation** - Each Known Value requires only 1-9
30///   bytes depending on value range
31/// - **Deterministic encoding** - Every concept has exactly one valid binary
32///   representation
33/// - **Enhanced security** - Eliminates URI manipulation vulnerabilities
34/// - **Standardized semantics** - Values are registered in a central registry
35///
36/// While Known Values are most commonly used as predicates in assertions, they
37/// can appear in any position in an Envelope (subject, predicate, or object).
38///
39/// # Examples
40///
41/// ```
42/// use known_values::KnownValue;
43///
44/// // Create a Known Value with a numeric value
45/// let known_value = KnownValue::new(42);
46/// assert_eq!(known_value.value(), 42);
47///
48/// // Create a Known Value with a name
49/// let named_value = KnownValue::new_with_name(1u64, "isA".to_string());
50/// assert_eq!(named_value.value(), 1);
51/// assert_eq!(named_value.name(), "isA");
52///
53/// // Use a pre-defined Known Value from the registry
54/// let is_a_value = known_values::IS_A;
55/// assert_eq!(is_a_value.value(), 1);
56/// assert_eq!(is_a_value.name(), "isA");
57/// ```
58///
59/// # Specification
60///
61/// Known Values are defined in
62/// [BCR-2023-002](https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2023-002-known-value.md)
63/// and implemented as an Envelope extension in
64/// [BCR-2023-003](https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2023-003-envelope-known-value.md).
65#[derive(Clone, Debug)]
66pub struct KnownValue {
67    /// The known value as coded into CBOR.
68    value: u64,
69    /// A name assigned to the known value used for debugging and formatted
70    /// output.
71    assigned_name: Option<KnownValueName>,
72}
73
74impl KnownValue {
75    /// Creates a new KnownValue with the given numeric value and no name.
76    ///
77    /// # Examples
78    ///
79    /// ```
80    /// use known_values::KnownValue;
81    ///
82    /// let known_value = KnownValue::new(42);
83    /// assert_eq!(known_value.value(), 42);
84    /// ```
85    pub fn new(value: u64) -> Self { Self { value, assigned_name: None } }
86
87    /// Creates a KnownValue with the given value and associated name.
88    ///
89    /// This function accepts any type that can be converted into a `u64` and
90    /// a String for the name. The name is stored as a dynamic value.
91    ///
92    /// # Examples
93    ///
94    /// ```
95    /// use known_values::KnownValue;
96    ///
97    /// let known_value = KnownValue::new_with_name(1u64, "isA".to_string());
98    /// assert_eq!(known_value.value(), 1);
99    /// assert_eq!(known_value.name(), "isA");
100    /// ```
101    pub fn new_with_name<T: Into<u64>>(
102        value: T,
103        assigned_name: String,
104    ) -> Self {
105        Self {
106            value: value.into(),
107            assigned_name: Some(KnownValueName::Dynamic(assigned_name)),
108        }
109    }
110
111    /// Creates a KnownValue at compile time with the given value and static
112    /// name.
113    ///
114    /// This function is used primarily with the `const_known_value!` macro to
115    /// define known values as constants in the registry.
116    ///
117    /// # Examples
118    ///
119    /// ```
120    /// use known_values::KnownValue;
121    ///
122    /// // This is similar to how registry constants are defined
123    /// const IS_A: KnownValue = KnownValue::new_with_static_name(1, "isA");
124    ///
125    /// assert_eq!(IS_A.value(), 1);
126    /// assert_eq!(IS_A.name(), "isA");
127    /// ```
128    pub const fn new_with_static_name(value: u64, name: &'static str) -> Self {
129        Self {
130            value,
131            assigned_name: Some(KnownValueName::Static(name)),
132        }
133    }
134
135    /// Returns the numeric value of the KnownValue.
136    ///
137    /// This is the raw 64-bit unsigned integer that identifies the concept.
138    ///
139    /// # Examples
140    ///
141    /// ```
142    /// assert_eq!(known_values::IS_A.value(), 1);
143    /// assert_eq!(known_values::NOTE.value(), 4);
144    /// ```
145    pub fn value(&self) -> u64 { self.value }
146
147    /// Returns the assigned name of the KnownValue, if one exists.
148    ///
149    /// # Examples
150    ///
151    /// ```
152    /// use known_values::KnownValue;
153    ///
154    /// let named_value = KnownValue::new_with_name(1u64, "isA".to_string());
155    /// assert_eq!(named_value.assigned_name(), Some("isA"));
156    ///
157    /// let unnamed_value = KnownValue::new(42);
158    /// assert_eq!(unnamed_value.assigned_name(), None);
159    /// ```
160    pub fn assigned_name(&self) -> Option<&str> {
161        match &self.assigned_name {
162            Some(KnownValueName::Static(name)) => Some(name),
163            Some(KnownValueName::Dynamic(name)) => Some(name),
164            None => None,
165        }
166    }
167
168    /// Returns a human-readable name for the KnownValue.
169    ///
170    /// If the KnownValue has an assigned name, that name is returned.
171    /// Otherwise, the string representation of the numeric value is returned.
172    ///
173    /// # Examples
174    ///
175    /// ```
176    /// use known_values::KnownValue;
177    ///
178    /// let named_value = KnownValue::new_with_name(1u64, "isA".to_string());
179    /// assert_eq!(named_value.name(), "isA");
180    ///
181    /// let unnamed_value = KnownValue::new(42);
182    /// assert_eq!(unnamed_value.name(), "42");
183    /// ```
184    pub fn name(&self) -> String {
185        match &self.assigned_name {
186            Some(KnownValueName::Static(name)) => name.to_string(),
187            Some(KnownValueName::Dynamic(name)) => name.clone(),
188            None => self.value.to_string(),
189        }
190    }
191}
192
193/// Equality for KnownValue is based solely on the numeric value, ignoring the
194/// name.
195impl PartialEq for KnownValue {
196    fn eq(&self, other: &Self) -> bool { self.value == other.value }
197}
198
199/// KnownValue implements Eq since equality is based on the numeric value, which
200/// can be compared for equality.
201impl Eq for KnownValue {}
202
203/// Hash implementation for KnownValue that considers only the numeric value.
204impl std::hash::Hash for KnownValue {
205    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
206        self.value.hash(state);
207    }
208}
209
210/// Formats the KnownValue for display.
211///
212/// If a name is assigned, the name is displayed. Otherwise, the numeric value
213/// is displayed.
214impl Display for KnownValue {
215    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
216        match &self.assigned_name {
217            Some(KnownValueName::Static(name)) => write!(f, "{}", name),
218            Some(KnownValueName::Dynamic(name)) => write!(f, "{}", name),
219            None => write!(f, "{}", self.value),
220        }
221    }
222}
223
224/// Provides a cryptographic digest for a KnownValue.
225impl DigestProvider for KnownValue {
226    fn digest(&self) -> Cow<'_, Digest> {
227        Cow::Owned(Digest::from_image(self.tagged_cbor().to_cbor_data()))
228    }
229}
230
231/// Specifies the CBOR tag used for KnownValue.
232impl CBORTagged for KnownValue {
233    fn cbor_tags() -> Vec<Tag> { tags_for_values(&[tags::TAG_KNOWN_VALUE]) }
234}
235
236/// Converts a KnownValue to CBOR.
237impl From<KnownValue> for CBOR {
238    fn from(value: KnownValue) -> Self { value.tagged_cbor() }
239}
240
241/// Attempts to convert CBOR to a KnownValue.
242impl TryFrom<CBOR> for KnownValue {
243    type Error = dcbor::Error;
244
245    fn try_from(cbor: CBOR) -> dcbor::Result<Self> {
246        Self::from_tagged_cbor(cbor)
247    }
248}
249
250/// Provides the untagged CBOR representation of a KnownValue.
251impl CBORTaggedEncodable for KnownValue {
252    fn untagged_cbor(&self) -> CBOR { self.value.into() }
253}
254
255/// Creates a KnownValue from untagged CBOR.
256impl CBORTaggedDecodable for KnownValue {
257    fn from_untagged_cbor(cbor: CBOR) -> dcbor::Result<Self> {
258        let value = u64::try_from(cbor)?;
259        Ok(Self::new(value))
260    }
261}
262
263/// Creates a KnownValue from a u64.
264impl From<u64> for KnownValue {
265    fn from(value: u64) -> Self { KnownValue::new(value) }
266}
267
268/// Creates a KnownValue from an i32.
269impl From<i32> for KnownValue {
270    fn from(value: i32) -> Self { KnownValue::new(value as u64) }
271}
272
273/// Creates a KnownValue from a usize.
274impl From<usize> for KnownValue {
275    fn from(value: usize) -> Self { KnownValue::new(value as u64) }
276}