bc_envelope/extension/expressions/
function.rs

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