bc_envelope/extension/expressions/
parameter.rs

1use bc_components::tags;
2use dcbor::prelude::*;
3
4use super::ParametersStore;
5use crate::{Envelope, EnvelopeEncodable, string_utils::StringUtils};
6
7/// Internal representation of a parameter name, which can be either static or
8/// dynamic.
9///
10/// Static names are &'static str references, typically used for compile-time
11/// constants. Dynamic names are owned String instances, used for
12/// runtime-created parameters.
13#[derive(Clone, Debug, Eq)]
14pub enum ParameterName {
15    /// A parameter name represented as a static string reference.
16    Static(&'static str),
17    /// A parameter name represented as a dynamic (owned) string.
18    Dynamic(String),
19}
20
21impl ParameterName {
22    /// Returns the string value of this parameter name.
23    fn value(&self) -> &str {
24        match self {
25            Self::Static(name) => name,
26            Self::Dynamic(name) => name,
27        }
28    }
29}
30
31/// Implementation of equality for ParameterName based on the string value.
32impl PartialEq for ParameterName {
33    fn eq(&self, other: &Self) -> bool { self.value() == other.value() }
34}
35
36/// Implementation of hashing for ParameterName based on the string value.
37impl std::hash::Hash for ParameterName {
38    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
39        self.value().hash(state)
40    }
41}
42
43/// A parameter identifier used in Gordian Envelope expressions.
44///
45/// In Gordian Envelope, a parameter appears as a predicate in an assertion on
46/// an expression envelope. The parameter identifies the name of the argument,
47/// and the object of the assertion is the argument value.
48///
49/// Parameters can be identified in two ways:
50/// 1. By a numeric ID (for well-known parameters)
51/// 2. By a string name (for application-specific or less common parameters)
52///
53/// When encoded in CBOR, parameters are tagged with #6.40007.
54///
55/// # Examples
56///
57/// A numeric parameter:
58///
59/// ```
60/// use bc_envelope::prelude::*;
61///
62/// // Define a parameter with a numeric ID
63/// let lhs_param = Parameter::new_known(2, Some("lhs".to_string()));
64/// ```
65///
66/// A named parameter:
67///
68/// ```
69/// use bc_envelope::prelude::*;
70///
71/// // Define a parameter with a string name
72/// let key_param = Parameter::new_named("key");
73/// ```
74///
75/// A parameter with static name:
76///
77/// ```
78/// use bc_envelope::prelude::*;
79///
80/// // Define a parameter with a numeric ID and static name
81/// const LHS: Parameter = Parameter::new_with_static_name(2, "lhs");
82/// ```
83#[derive(Clone, Debug, Eq)]
84pub enum Parameter {
85    /// A well-known parameter identified by a numeric ID, with an optional
86    /// name.
87    Known(u64, Option<ParameterName>),
88    /// A parameter identified by a name.
89    Named(ParameterName),
90}
91
92impl Parameter {
93    /// Creates a new parameter with a numeric ID and an optional string name.
94    ///
95    /// This creates a "known" parameter, which is identified primarily by its
96    /// numeric ID. The optional name is used for display purposes.
97    ///
98    /// # Parameters
99    ///
100    /// * `value` - The numeric ID of the parameter
101    /// * `name` - An optional name for the parameter
102    ///
103    /// # Returns
104    ///
105    /// A new `Parameter` instance
106    ///
107    /// # Examples
108    ///
109    /// ```
110    /// use bc_envelope::prelude::*;
111    ///
112    /// // Create a parameter with ID 2 and name "lhs"
113    /// let lhs_param = Parameter::new_known(2, Some("lhs".to_string()));
114    /// ```
115    pub fn new_known(value: u64, name: Option<String>) -> Self {
116        Self::Known(value, name.map(ParameterName::Dynamic))
117    }
118
119    /// Creates a new parameter identified by a string name.
120    ///
121    /// This creates a "named" parameter, which is identified by its string
122    /// name. This method cannot be used to declare a parameter at
123    /// compile-time, as it creates a dynamic string.
124    ///
125    /// # Parameters
126    ///
127    /// * `name` - The string name of the parameter
128    ///
129    /// # Returns
130    ///
131    /// A new `Parameter` instance
132    ///
133    /// # Examples
134    ///
135    /// ```
136    /// use bc_envelope::prelude::*;
137    ///
138    /// // Create a named parameter
139    /// let key_param = Parameter::new_named("key");
140    /// ```
141    pub fn new_named(name: &str) -> Self {
142        Self::Named(ParameterName::Dynamic(name.into()))
143    }
144
145    /// Creates a new parameter with a numeric ID and a static string name.
146    ///
147    /// This creates a "known" parameter, which is identified primarily by its
148    /// numeric ID. The static name is used for display purposes. This
149    /// method can be used to declare a parameter at compile-time, as it
150    /// uses a static string reference.
151    ///
152    /// # Parameters
153    ///
154    /// * `value` - The numeric ID of the parameter
155    /// * `name` - A static string name for the parameter
156    ///
157    /// # Returns
158    ///
159    /// A new `Parameter` instance
160    ///
161    /// # Examples
162    ///
163    /// ```
164    /// use bc_envelope::prelude::*;
165    ///
166    /// // Define a parameter constant
167    /// const LHS: Parameter = Parameter::new_with_static_name(2, "lhs");
168    /// ```
169    pub const fn new_with_static_name(value: u64, name: &'static str) -> Self {
170        Self::Known(value, Some(ParameterName::Static(name)))
171    }
172
173    /// Creates a new parameter identified by a static string name.
174    ///
175    /// This creates a "named" parameter, which is identified by its string
176    /// name. This method can be used to declare a parameter at
177    /// compile-time, as it uses a static string reference.
178    ///
179    /// # Parameters
180    ///
181    /// * `name` - A static string name for the parameter
182    ///
183    /// # Returns
184    ///
185    /// A new `Parameter` instance
186    ///
187    /// # Examples
188    ///
189    /// ```
190    /// use bc_envelope::prelude::*;
191    ///
192    /// // Define a named parameter constant
193    /// const KEY: Parameter = Parameter::new_static_named("key");
194    /// ```
195    pub const fn new_static_named(name: &'static str) -> Self {
196        Self::Named(ParameterName::Static(name))
197    }
198
199    /// Returns the display name of the parameter.
200    ///
201    /// For known parameters with a name, returns the name.
202    /// For known parameters without a name, returns the numeric ID as a string.
203    /// For named parameters, returns the name enclosed in quotes.
204    ///
205    /// # Returns
206    ///
207    /// A string representation of the parameter name
208    ///
209    /// # Examples
210    ///
211    /// ```
212    /// use bc_envelope::prelude::*;
213    ///
214    /// let lhs = Parameter::new_known(2, Some("lhs".to_string()));
215    /// assert_eq!(lhs.name(), "lhs");
216    ///
217    /// let unknown = Parameter::new_known(42, None);
218    /// assert_eq!(unknown.name(), "42");
219    ///
220    /// let key = Parameter::new_named("key");
221    /// assert_eq!(key.name(), "\"key\"");
222    /// ```
223    pub fn name(&self) -> String {
224        match self {
225            Self::Known(value, name) => {
226                if let Some(name) = name {
227                    name.value().to_string()
228                } else {
229                    value.to_string()
230                }
231            }
232            Self::Named(name) => {
233                name.value().to_string().flanked_by("\"", "\"")
234            }
235        }
236    }
237}
238
239/// Implementation of equality for Parameter.
240///
241/// Known parameters are equal if they have the same numeric ID (names are
242/// ignored). Named parameters are equal if they have the same name.
243/// Known and named parameters are never equal to each other.
244impl PartialEq for Parameter {
245    fn eq(&self, other: &Self) -> bool {
246        match (self, other) {
247            (Self::Known(l, _), Self::Known(r, _)) => l == r,
248            (Self::Named(l), Self::Named(r)) => l == r,
249            _ => false,
250        }
251    }
252}
253
254/// Implementation of hash for Parameter.
255///
256/// Known parameters are hashed by their numeric ID.
257/// Named parameters are hashed by their name.
258impl std::hash::Hash for Parameter {
259    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
260        match self {
261            Self::Known(value, _) => value.hash(state),
262            Self::Named(name) => name.hash(state),
263        }
264    }
265}
266
267/// Allows creating a Parameter from a u64.
268///
269/// This creates a known parameter with the given numeric ID and no name.
270impl From<u64> for Parameter {
271    fn from(value: u64) -> Self { Self::new_known(value, None) }
272}
273
274/// Allows creating a Parameter from a string reference.
275///
276/// This creates a named parameter with the given name.
277impl From<&str> for Parameter {
278    fn from(name: &str) -> Self { Self::new_named(name) }
279}
280
281/// Allows creating a Parameter from a reference to a Parameter.
282///
283/// This clones the parameter.
284impl From<&Parameter> for Parameter {
285    fn from(parameter: &Parameter) -> Self { parameter.clone() }
286}
287
288/// Implementation of the CBORTagged trait for Parameter.
289///
290/// Parameters are tagged with #6.40007 (TAG_PARAMETER).
291impl CBORTagged for Parameter {
292    fn cbor_tags() -> Vec<Tag> { tags_for_values(&[tags::TAG_PARAMETER]) }
293}
294
295/// Allows creating a CBOR value from a Parameter.
296impl From<Parameter> for CBOR {
297    fn from(value: Parameter) -> Self { value.tagged_cbor() }
298}
299
300/// Implementation of the CBORTaggedEncodable trait for Parameter.
301///
302/// Known parameters are encoded as unsigned integers.
303/// Named parameters are encoded as text strings.
304impl CBORTaggedEncodable for Parameter {
305    fn untagged_cbor(&self) -> CBOR {
306        match self {
307            Parameter::Known(value, _) => (*value).into(),
308            Parameter::Named(name) => name.value().into(),
309        }
310    }
311}
312
313/// Allows creating a Parameter from a CBOR value.
314impl TryFrom<CBOR> for Parameter {
315    type Error = dcbor::Error;
316
317    fn try_from(cbor: CBOR) -> dcbor::Result<Self> {
318        Self::from_tagged_cbor(cbor)
319    }
320}
321
322/// Implementation of the CBORTaggedDecodable trait for Parameter.
323///
324/// Unsigned integers are decoded as known parameters.
325/// Text strings are decoded as named parameters.
326impl CBORTaggedDecodable for Parameter {
327    fn from_untagged_cbor(untagged_cbor: CBOR) -> dcbor::Result<Self> {
328        match untagged_cbor.as_case() {
329            CBORCase::Unsigned(value) => Ok(Self::new_known(*value, None)),
330            CBORCase::Text(name) => Ok(Self::new_named(name)),
331            _ => Err("invalid parameter".into()),
332        }
333    }
334}
335
336impl Parameter {
337    /// Returns a description of this parameter for display.
338    ///
339    /// For known parameters, attempts to look up the name in the provided
340    /// parameters store, if available.
341    /// For named parameters, returns the name in quotes.
342    fn description(&self, parameters: Option<&ParametersStore>) -> String {
343        match self {
344            Parameter::Known(_, _) => {
345                ParametersStore::name_for_parameter(self, parameters)
346            }
347            Parameter::Named(name) => {
348                format!("\"{}\"", name.value())
349            }
350        }
351    }
352}
353
354/// Implements display for Parameter.
355///
356/// Uses the description method with no parameters store.
357impl std::fmt::Display for Parameter {
358    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
359        write!(f, "{}", self.description(None))
360    }
361}
362
363/// Implements the EnvelopeEncodable trait for Parameter.
364///
365/// This allows a Parameter to be directly converted to an Envelope.
366impl EnvelopeEncodable for Parameter {
367    fn into_envelope(self) -> Envelope { Envelope::new_leaf(self) }
368}