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}