known_values/
known_value.rs

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