known_values/known_value.rs
1use std::fmt::{Display, Formatter};
2
3use bc_components::{Digest, DigestProvider, tags};
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
20/// to 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
67 /// output.
68 assigned_name: Option<KnownValueName>,
69}
70
71impl KnownValue {
72 /// Creates a new KnownValue with the given numeric value and no name.
73 ///
74 /// # Examples
75 ///
76 /// ```
77 /// use known_values::KnownValue;
78 ///
79 /// let known_value = KnownValue::new(42);
80 /// assert_eq!(known_value.value(), 42);
81 /// ```
82 pub fn new(value: u64) -> Self { Self { value, assigned_name: None } }
83
84 /// Creates a KnownValue with the given value and associated name.
85 ///
86 /// This function accepts any type that can be converted into a `u64` and
87 /// a String for the name. The name is stored as a dynamic value.
88 ///
89 /// # Examples
90 ///
91 /// ```
92 /// use known_values::KnownValue;
93 ///
94 /// let known_value = KnownValue::new_with_name(1u64, "isA".to_string());
95 /// assert_eq!(known_value.value(), 1);
96 /// assert_eq!(known_value.name(), "isA");
97 /// ```
98 pub fn new_with_name<T: Into<u64>>(
99 value: T,
100 assigned_name: String,
101 ) -> Self {
102 Self {
103 value: value.into(),
104 assigned_name: Some(KnownValueName::Dynamic(assigned_name)),
105 }
106 }
107
108 /// Creates a KnownValue at compile time with the given value and static
109 /// name.
110 ///
111 /// This function is used primarily with the `const_known_value!` macro to
112 /// define known values as constants in the registry.
113 ///
114 /// # Examples
115 ///
116 /// ```
117 /// use known_values::KnownValue;
118 ///
119 /// // This is similar to how registry constants are defined
120 /// const IS_A: KnownValue = KnownValue::new_with_static_name(1, "isA");
121 ///
122 /// assert_eq!(IS_A.value(), 1);
123 /// assert_eq!(IS_A.name(), "isA");
124 /// ```
125 pub const fn new_with_static_name(value: u64, name: &'static str) -> Self {
126 Self {
127 value,
128 assigned_name: Some(KnownValueName::Static(name)),
129 }
130 }
131
132 /// Returns the numeric value of the KnownValue.
133 ///
134 /// This is the raw 64-bit unsigned integer that identifies the concept.
135 ///
136 /// # Examples
137 ///
138 /// ```
139 /// assert_eq!(known_values::IS_A.value(), 1);
140 /// assert_eq!(known_values::NOTE.value(), 4);
141 /// ```
142 pub fn value(&self) -> u64 { self.value }
143
144 /// Returns the assigned name of the KnownValue, if one exists.
145 ///
146 /// # Examples
147 ///
148 /// ```
149 /// use known_values::KnownValue;
150 ///
151 /// let named_value = KnownValue::new_with_name(1u64, "isA".to_string());
152 /// assert_eq!(named_value.assigned_name(), Some("isA"));
153 ///
154 /// let unnamed_value = KnownValue::new(42);
155 /// assert_eq!(unnamed_value.assigned_name(), None);
156 /// ```
157 pub fn assigned_name(&self) -> Option<&str> {
158 match &self.assigned_name {
159 Some(KnownValueName::Static(name)) => Some(name),
160 Some(KnownValueName::Dynamic(name)) => Some(name),
161 None => None,
162 }
163 }
164
165 /// Returns a human-readable name for the KnownValue.
166 ///
167 /// If the KnownValue has an assigned name, that name is returned.
168 /// Otherwise, the string representation of the numeric value is returned.
169 ///
170 /// # Examples
171 ///
172 /// ```
173 /// use known_values::KnownValue;
174 ///
175 /// let named_value = KnownValue::new_with_name(1u64, "isA".to_string());
176 /// assert_eq!(named_value.name(), "isA");
177 ///
178 /// let unnamed_value = KnownValue::new(42);
179 /// assert_eq!(unnamed_value.name(), "42");
180 /// ```
181 pub fn name(&self) -> String {
182 match &self.assigned_name {
183 Some(KnownValueName::Static(name)) => name.to_string(),
184 Some(KnownValueName::Dynamic(name)) => name.clone(),
185 None => self.value.to_string(),
186 }
187 }
188}
189
190/// Equality for KnownValue is based solely on the numeric value, ignoring the
191/// name.
192impl PartialEq for KnownValue {
193 fn eq(&self, other: &Self) -> bool { self.value == other.value }
194}
195
196/// KnownValue implements Eq since equality is based on the numeric value, which
197/// can be compared for equality.
198impl Eq for KnownValue {}
199
200/// Hash implementation for KnownValue that considers only the numeric value.
201impl std::hash::Hash for KnownValue {
202 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
203 self.value.hash(state);
204 }
205}
206
207/// Formats the KnownValue for display.
208///
209/// If a name is assigned, the name is displayed. Otherwise, the numeric value
210/// is displayed.
211impl Display for KnownValue {
212 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
213 match &self.assigned_name {
214 Some(KnownValueName::Static(name)) => write!(f, "{}", name),
215 Some(KnownValueName::Dynamic(name)) => write!(f, "{}", name),
216 None => write!(f, "{}", self.value),
217 }
218 }
219}
220
221/// Provides a cryptographic digest for a KnownValue.
222impl DigestProvider for KnownValue {
223 fn digest(&self) -> Digest {
224 Digest::from_image(self.tagged_cbor().to_cbor_data())
225 }
226}
227
228/// Specifies the CBOR tag used for KnownValue.
229impl CBORTagged for KnownValue {
230 fn cbor_tags() -> Vec<Tag> { tags_for_values(&[tags::TAG_KNOWN_VALUE]) }
231}
232
233/// Converts a KnownValue to CBOR.
234impl From<KnownValue> for CBOR {
235 fn from(value: KnownValue) -> Self { value.tagged_cbor() }
236}
237
238/// Attempts to convert CBOR to a KnownValue.
239impl TryFrom<CBOR> for KnownValue {
240 type Error = dcbor::Error;
241
242 fn try_from(cbor: CBOR) -> dcbor::Result<Self> {
243 Self::from_tagged_cbor(cbor)
244 }
245}
246
247/// Provides the untagged CBOR representation of a KnownValue.
248impl CBORTaggedEncodable for KnownValue {
249 fn untagged_cbor(&self) -> CBOR { self.value.into() }
250}
251
252/// Creates a KnownValue from untagged CBOR.
253impl CBORTaggedDecodable for KnownValue {
254 fn from_untagged_cbor(cbor: CBOR) -> dcbor::Result<Self> {
255 let value = u64::try_from(cbor)?;
256 Ok(Self::new(value))
257 }
258}
259
260/// Creates a KnownValue from a u64.
261impl From<u64> for KnownValue {
262 fn from(value: u64) -> Self { KnownValue::new(value) }
263}
264
265/// Creates a KnownValue from an i32.
266impl From<i32> for KnownValue {
267 fn from(value: i32) -> Self { KnownValue::new(value as u64) }
268}
269
270/// Creates a KnownValue from a usize.
271impl From<usize> for KnownValue {
272 fn from(value: usize) -> Self { KnownValue::new(value as u64) }
273}