bc_envelope/extension/expressions/
function.rs

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