bc_envelope/extension/expressions/
expression.rs

1use dcbor::prelude::*;
2
3use crate::{Envelope, EnvelopeEncodable, Function, Parameter, Result};
4
5/// An expression in a Gordian Envelope.
6///
7/// An expression consists of a function (the subject of the envelope) and zero
8/// or more parameters (as assertions on the envelope). It represents a
9/// computation or function call that can be evaluated.
10///
11/// Expressions form the foundation for Gordian Envelope's ability to represent
12/// computations, queries, and function calls within the envelope structure.
13///
14/// Expressions are documented in [BCR-2023-012: Envelope
15/// Expressions](https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2023-012-envelope-expression.md).
16///
17/// # Examples
18///
19/// A simple addition expression:
20///
21/// ```
22/// use bc_envelope::prelude::*;
23///
24/// // Create an expression that adds 2 and 3
25/// let expression = Expression::new(functions::ADD)
26///     .with_parameter(parameters::LHS, 2)
27///     .with_parameter(parameters::RHS, 3);
28/// ```
29///
30/// A more complex expression:
31///
32/// ```ignore
33/// use bc_envelope::prelude::*;
34///
35/// // Create a verify signature expression with a public key, signature, and digest
36/// let expression = Expression::new("verifySignature")
37///     .with_parameter("key", public_key)
38///     .with_parameter("sig", signature)
39///     .with_parameter("digest", message_digest);
40/// ```
41#[derive(Debug, Clone, PartialEq)]
42pub struct Expression {
43    /// The function being called in this expression
44    function: Function,
45    /// The envelope representing this expression
46    envelope: Envelope,
47}
48
49impl Expression {
50    /// Creates a new expression with the given function.
51    ///
52    /// The function becomes the subject of the expression envelope.
53    ///
54    /// # Parameters
55    ///
56    /// * `function` - The function identifier for this expression
57    ///
58    /// # Returns
59    ///
60    /// A new Expression instance
61    ///
62    /// # Examples
63    ///
64    /// ```
65    /// use bc_envelope::prelude::*;
66    ///
67    /// // Create a new expression with the ADD function
68    /// let expression = Expression::new(functions::ADD);
69    ///
70    /// // Create a new expression with a named function
71    /// let expression = Expression::new("verifySignature");
72    /// ```
73    pub fn new(function: impl Into<Function>) -> Self {
74        let function = function.into();
75        Self {
76            function: function.clone(),
77            envelope: Envelope::new(function),
78        }
79    }
80}
81
82/// Implements Display for Expression.
83///
84/// Outputs the formatted envelope representation of the expression.
85impl std::fmt::Display for Expression {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        write!(f, "{:?}", self.envelope.format())
88    }
89}
90
91/// Behavior for working with expressions.
92///
93/// This trait defines methods for composing expressions by adding parameters,
94/// and for parsing and extracting values from expression parameters.
95///
96/// ExpressionBehavior is implemented by Expression and other types that
97/// represent expressions in Gordian Envelope.
98pub trait ExpressionBehavior {
99    //
100    // Composition
101    //
102
103    /// Adds a parameter to the expression.
104    ///
105    /// This creates an assertion on the expression envelope with the parameter
106    /// as the predicate and the value as the object.
107    ///
108    /// # Parameters
109    ///
110    /// * `parameter` - The parameter identifier
111    /// * `value` - The value for the parameter
112    ///
113    /// # Returns
114    ///
115    /// A new instance with the parameter added
116    ///
117    /// # Examples
118    ///
119    /// ```
120    /// use bc_envelope::prelude::*;
121    ///
122    /// let expression = Expression::new(functions::ADD)
123    ///     .with_parameter(parameters::LHS, 2)
124    ///     .with_parameter(parameters::RHS, 3);
125    /// ```
126    fn with_parameter(
127        self,
128        parameter: impl Into<Parameter>,
129        value: impl EnvelopeEncodable,
130    ) -> Self;
131
132    /// Adds a parameter to the expression if the value is not `None`.
133    ///
134    /// If the value is `Some`, this creates an assertion on the expression
135    /// envelope with the parameter as the predicate and the value as the
136    /// object. If the value is `None`, the parameter is not added.
137    ///
138    /// # Parameters
139    ///
140    /// * `parameter` - The parameter identifier
141    /// * `value` - An optional value for the parameter
142    ///
143    /// # Returns
144    ///
145    /// A new instance with the parameter added if the value is Some
146    ///
147    /// # Examples
148    ///
149    /// ```
150    /// use bc_envelope::prelude::*;
151    ///
152    /// let expression = Expression::new(functions::ADD)
153    ///     .with_parameter(parameters::LHS, 2)
154    ///     .with_optional_parameter(parameters::RHS, Some(3))
155    ///     .with_optional_parameter("note", None::<&str>);
156    /// ```
157    fn with_optional_parameter(
158        self,
159        parameter: impl Into<Parameter>,
160        value: Option<impl EnvelopeEncodable>,
161    ) -> Self;
162
163    //
164    // Parsing
165    //
166
167    /// Returns the function identifier of the expression.
168    ///
169    /// # Returns
170    ///
171    /// A reference to the Function of this expression
172    fn function(&self) -> &Function;
173
174    /// Returns the envelope that represents this expression.
175    ///
176    /// # Returns
177    ///
178    /// A reference to the Envelope of this expression
179    fn expression_envelope(&self) -> &Envelope;
180
181    /// Returns the argument (object) for the given parameter.
182    ///
183    /// # Parameters
184    ///
185    /// * `param` - The parameter to look up
186    ///
187    /// # Returns
188    ///
189    /// The argument envelope for the parameter, or an error if not found
190    ///
191    /// # Errors
192    ///
193    /// Returns an error if no matching parameter is found or if multiple
194    /// parameters match.
195    fn object_for_parameter(
196        &self,
197        param: impl Into<Parameter>,
198    ) -> Result<Envelope>;
199
200    /// Returns all arguments (objects) for the given parameter.
201    ///
202    /// This method handles the case where a parameter appears multiple times.
203    ///
204    /// # Parameters
205    ///
206    /// * `param` - The parameter to look up
207    ///
208    /// # Returns
209    ///
210    /// A vector of all matching argument envelopes, or an empty vector if none
211    /// are found
212    fn objects_for_parameter(
213        &self,
214        param: impl Into<Parameter>,
215    ) -> Vec<Envelope>;
216
217    /// Returns the argument for the given parameter, decoded as the given type.
218    ///
219    /// # Parameters
220    ///
221    /// * `param` - The parameter to look up
222    ///
223    /// # Returns
224    ///
225    /// The argument for the parameter, decoded as type T
226    ///
227    /// # Errors
228    ///
229    /// Returns an error if:
230    /// - No matching parameter is found
231    /// - Multiple parameters match
232    /// - The argument cannot be decoded as type T
233    fn extract_object_for_parameter<T>(
234        &self,
235        param: impl Into<Parameter>,
236    ) -> Result<T>
237    where
238        T: TryFrom<CBOR, Error = dcbor::Error> + 'static;
239
240    /// Returns the argument for the given parameter, decoded as the given type,
241    /// or `None` if there is no matching parameter.
242    ///
243    /// # Parameters
244    ///
245    /// * `param` - The parameter to look up
246    ///
247    /// # Returns
248    ///
249    /// Some(T) if the parameter is found and can be decoded, or None if not
250    /// found
251    ///
252    /// # Errors
253    ///
254    /// Returns an error if:
255    /// - Multiple parameters match
256    /// - The argument cannot be decoded as type T
257    fn extract_optional_object_for_parameter<
258        T: TryFrom<CBOR, Error = dcbor::Error> + 'static,
259    >(
260        &self,
261        param: impl Into<Parameter>,
262    ) -> Result<Option<T>>;
263
264    /// Returns an array of arguments for the given parameter, decoded as the
265    /// given type.
266    ///
267    /// This method handles the case where a parameter appears multiple times.
268    ///
269    /// # Parameters
270    ///
271    /// * `param` - The parameter to look up
272    ///
273    /// # Returns
274    ///
275    /// A vector of all matching arguments, decoded as type T
276    ///
277    /// # Errors
278    ///
279    /// Returns an error if any of the arguments cannot be decoded as type T
280    fn extract_objects_for_parameter<T>(
281        &self,
282        param: impl Into<Parameter>,
283    ) -> Result<Vec<T>>
284    where
285        T: TryFrom<CBOR, Error = dcbor::Error> + 'static;
286}
287
288/// Implementation of ExpressionBehavior for Expression.
289impl ExpressionBehavior for Expression {
290    /// Adds a parameter to the expression.
291    fn with_parameter(
292        mut self,
293        parameter: impl Into<Parameter>,
294        value: impl EnvelopeEncodable,
295    ) -> Self {
296        let assertion =
297            Envelope::new_assertion(parameter.into(), value.into_envelope());
298        self.envelope =
299            self.envelope.add_assertion_envelope(assertion).unwrap();
300        self
301    }
302
303    /// Adds a parameter to the expression if the value is not None.
304    fn with_optional_parameter(
305        self,
306        parameter: impl Into<Parameter>,
307        value: Option<impl EnvelopeEncodable>,
308    ) -> Self {
309        if let Some(value) = value {
310            return self.with_parameter(parameter, value);
311        }
312        self
313    }
314
315    /// Returns the function of the expression.
316    fn function(&self) -> &Function { &self.function }
317
318    /// Returns the envelope representing the expression.
319    fn expression_envelope(&self) -> &Envelope { &self.envelope }
320
321    /// Returns the argument for the given parameter.
322    fn object_for_parameter(
323        &self,
324        param: impl Into<Parameter>,
325    ) -> Result<Envelope> {
326        self.envelope.object_for_predicate(param.into())
327    }
328
329    /// Returns all arguments for the given parameter.
330    fn objects_for_parameter(
331        &self,
332        param: impl Into<Parameter>,
333    ) -> Vec<Envelope> {
334        self.envelope.objects_for_predicate(param.into())
335    }
336
337    /// Returns the argument for the given parameter, decoded as the given type.
338    fn extract_object_for_parameter<T>(
339        &self,
340        param: impl Into<Parameter>,
341    ) -> Result<T>
342    where
343        T: TryFrom<CBOR, Error = dcbor::Error> + 'static,
344    {
345        self.envelope.extract_object_for_predicate(param.into())
346    }
347
348    /// Returns the argument for the given parameter, decoded as the given type,
349    /// or None if there is no matching parameter.
350    fn extract_optional_object_for_parameter<
351        T: TryFrom<CBOR, Error = dcbor::Error> + 'static,
352    >(
353        &self,
354        param: impl Into<Parameter>,
355    ) -> Result<Option<T>> {
356        self.envelope
357            .extract_optional_object_for_predicate(param.into())
358    }
359
360    /// Returns an array of arguments for the given parameter, decoded as the
361    /// given type.
362    fn extract_objects_for_parameter<T>(
363        &self,
364        param: impl Into<Parameter>,
365    ) -> Result<Vec<T>>
366    where
367        T: TryFrom<CBOR, Error = dcbor::Error> + 'static,
368    {
369        self.envelope.extract_objects_for_predicate(param.into())
370    }
371}
372
373/// Allows converting an Expression to an Envelope.
374///
375/// This simply returns the envelope that represents the expression.
376impl From<Expression> for Envelope {
377    fn from(expression: Expression) -> Self { expression.envelope }
378}
379
380/// Allows converting an Envelope to an Expression.
381///
382/// This extracts the function from the envelope's subject and creates
383/// an Expression with that function and the envelope.
384///
385/// # Errors
386///
387/// Returns an error if the envelope's subject cannot be extracted as a
388/// Function.
389impl TryFrom<Envelope> for Expression {
390    type Error = dcbor::Error;
391
392    fn try_from(envelope: Envelope) -> dcbor::Result<Self> {
393        let function = envelope.extract_subject().map_err(|e| e.to_string())?;
394        Ok(Self { function, envelope })
395    }
396}
397
398/// Allows converting an Envelope and optional expected function to an
399/// Expression.
400///
401/// This is similar to `TryFrom<Envelope>`, but it also checks that the function
402/// matches the expected function, if provided.
403///
404/// # Errors
405///
406/// Returns an error if:
407/// - The envelope's subject cannot be extracted as a Function
408/// - The expected function is provided and doesn't match the extracted function
409impl TryFrom<(Envelope, Option<&Function>)> for Expression {
410    type Error = dcbor::Error;
411
412    fn try_from(
413        (envelope, expected_function): (Envelope, Option<&Function>),
414    ) -> dcbor::Result<Self> {
415        let expression = Expression::try_from(envelope)?;
416        if let Some(expected_function) = expected_function {
417            if expression.function() != expected_function {
418                return Err(format!(
419                    "Expected function {:?}, but found {:?}",
420                    expected_function,
421                    expression.function()
422                )
423                .into());
424            }
425        }
426        Ok(expression)
427    }
428}
429
430/// A trait for converting types to Expression.
431///
432/// This trait provides convenience methods for converting a type to
433/// an Expression, either by consuming it or by cloning it.
434pub trait IntoExpression {
435    /// Converts this object into an Expression, consuming it.
436    fn into_expression(self) -> Expression;
437
438    /// Creates an Expression from this object, without consuming it.
439    fn to_expression(&self) -> Expression;
440}
441
442/// Implementation of IntoExpression for any type that can be converted to
443/// Expression.
444///
445/// This allows any type that implements `Into<Expression>` to be used with the
446/// convenience methods provided by the IntoExpression trait.
447impl<T: Into<Expression> + Clone> IntoExpression for T {
448    /// Converts this object into an Expression, consuming it.
449    fn into_expression(self) -> Expression { self.into() }
450
451    /// Creates an Expression from this object, without consuming it.
452    fn to_expression(&self) -> Expression { self.clone().into() }
453}
454
455#[cfg(test)]
456mod tests {
457    use indoc::indoc;
458
459    use super::*;
460    use crate::{functions, parameters};
461
462    #[test]
463    fn test_expression_1() -> Result<()> {
464        crate::register_tags();
465
466        let expression = Expression::new(functions::ADD)
467            .with_parameter(parameters::LHS, 2)
468            .with_parameter(parameters::RHS, 3);
469
470        let envelope: Envelope = expression.clone().into();
471
472        #[rustfmt::skip]
473        let expected = indoc! {r#"
474            «add» [
475                ❰lhs❱: 2
476                ❰rhs❱: 3
477            ]
478        "#}.trim();
479        assert_eq!(envelope.format(), expected);
480
481        let parsed_expression = Expression::try_from(envelope)?;
482
483        assert_eq!(
484            parsed_expression
485                .extract_object_for_parameter::<i32>(parameters::LHS)?,
486            2
487        );
488        assert_eq!(
489            parsed_expression
490                .extract_object_for_parameter::<i32>(parameters::RHS)?,
491            3
492        );
493
494        assert_eq!(parsed_expression.function(), expression.function());
495        assert_eq!(
496            parsed_expression.expression_envelope(),
497            expression.expression_envelope()
498        );
499        assert_eq!(expression, parsed_expression);
500
501        Ok(())
502    }
503
504    #[test]
505    fn test_expression_2() -> Result<()> {
506        crate::register_tags();
507
508        let expression = Expression::new("foo")
509            .with_parameter("bar", "baz")
510            .with_optional_parameter("qux", None::<&str>);
511
512        let envelope: Envelope = expression.clone().into();
513
514        #[rustfmt::skip]
515        let expected = indoc! {r#"
516            «"foo"» [
517                ❰"bar"❱: "baz"
518            ]
519        "#}.trim();
520        assert_eq!(envelope.format(), expected);
521
522        let parsed_expression = Expression::try_from(envelope)?;
523
524        assert_eq!(
525            parsed_expression.extract_object_for_parameter::<String>("bar")?,
526            "baz"
527        );
528        assert_eq!(
529            parsed_expression
530                .extract_optional_object_for_parameter::<i32>("qux")?,
531            None
532        );
533
534        assert_eq!(parsed_expression.function(), expression.function());
535        assert_eq!(
536            parsed_expression.expression_envelope(),
537            expression.expression_envelope()
538        );
539        assert_eq!(expression, parsed_expression);
540
541        Ok(())
542    }
543}