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}