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}