toasty_core/stmt/
input.rs

1use crate::{
2    Schema,
3    stmt::{Expr, ExprArg, ExprContext, ExprReference, Project, Projection, Resolve, Type, Value},
4};
5
6/// Provides runtime argument and reference resolution for expression
7/// evaluation and substitution.
8///
9/// During expression evaluation, `Arg` and `Reference` nodes are resolved
10/// by calling methods on an `Input` implementation. The default methods
11/// return `None` (unresolved).
12///
13/// # Examples
14///
15/// ```
16/// use toasty_core::stmt::{ConstInput, Input};
17///
18/// // ConstInput resolves nothing -- suitable for expressions with no
19/// // external arguments.
20/// let mut input = ConstInput::new();
21/// ```
22pub trait Input {
23    /// Resolves an argument expression at the given projection.
24    ///
25    /// Returns `Some(expr)` if the argument can be resolved, or `None`
26    /// if it cannot.
27    fn resolve_arg(&mut self, expr_arg: &ExprArg, projection: &Projection) -> Option<Expr> {
28        let _ = (expr_arg, projection);
29        None
30    }
31
32    /// Resolves a reference expression at the given projection.
33    ///
34    /// Returns `Some(expr)` if the reference can be resolved, or `None`
35    /// if it cannot.
36    fn resolve_ref(
37        &mut self,
38        expr_reference: &ExprReference,
39        projection: &Projection,
40    ) -> Option<Expr> {
41        let _ = (expr_reference, projection);
42        None
43    }
44}
45
46/// An [`Input`] implementation that resolves nothing.
47///
48/// Use `ConstInput` when evaluating expressions that contain no external
49/// arguments or references (i.e., constant expressions).
50///
51/// # Examples
52///
53/// ```
54/// use toasty_core::stmt::{ConstInput, Value, Expr};
55///
56/// let expr = Expr::from(Value::from(42_i64));
57/// let result = expr.eval(ConstInput::new()).unwrap();
58/// assert_eq!(result, Value::from(42_i64));
59/// ```
60#[derive(Debug, Default)]
61pub struct ConstInput {}
62
63/// An [`Input`] wrapper that validates resolved argument types against
64/// expected types at resolution time.
65///
66/// `TypedInput` delegates resolution to an inner `Input` and then checks
67/// that the resolved expression's inferred type is a subtype of the
68/// expected argument type from `tys`.
69pub struct TypedInput<'a, I, T = Schema> {
70    cx: ExprContext<'a, T>,
71    tys: &'a [Type],
72    input: I,
73}
74
75impl ConstInput {
76    /// Creates a new `ConstInput`.
77    pub fn new() -> ConstInput {
78        ConstInput {}
79    }
80}
81
82impl Input for ConstInput {}
83
84impl<'a, I, T> TypedInput<'a, I, T> {
85    /// Creates a new `TypedInput` with the given expression context,
86    /// expected argument types, and inner input.
87    pub fn new(cx: ExprContext<'a, T>, tys: &'a [Type], input: I) -> Self {
88        TypedInput { cx, tys, input }
89    }
90}
91
92impl<I: Input, T: Resolve> Input for TypedInput<'_, I, T> {
93    fn resolve_arg(&mut self, expr_arg: &ExprArg, projection: &Projection) -> Option<Expr> {
94        let expr = self.input.resolve_arg(expr_arg, projection)?;
95
96        if !expr.is_value_null() {
97            let actual_ty = self.cx.infer_expr_ty(&expr, &[]);
98
99            let mut ty = &self.tys[expr_arg.position];
100
101            for step in projection {
102                ty = match ty {
103                    Type::Record(tys) => &tys[step],
104                    Type::List(item) => item,
105                    _ => todo!("ty={ty:#?}"),
106                };
107            }
108
109            assert!(
110                actual_ty.is_subtype_of(ty),
111                "resolved input did not match requested argument type; expected={ty:#?}; actual={actual_ty:#?}"
112            )
113        }
114
115        Some(expr)
116    }
117}
118
119impl Input for &Vec<Value> {
120    fn resolve_arg(&mut self, expr_arg: &ExprArg, projection: &Projection) -> Option<Expr> {
121        Some(self[expr_arg.position].entry(projection).to_expr())
122    }
123}
124
125impl<T, const N: usize> Input for [T; N]
126where
127    for<'a> &'a T: Project,
128{
129    fn resolve_arg(&mut self, expr_arg: &ExprArg, projection: &Projection) -> Option<Expr> {
130        (&self[expr_arg.position]).project(projection)
131    }
132}
133
134impl<T, const N: usize> Input for &[T; N]
135where
136    for<'a> &'a T: Project,
137{
138    fn resolve_arg(&mut self, expr_arg: &ExprArg, projection: &Projection) -> Option<Expr> {
139        (&self[expr_arg.position]).project(projection)
140    }
141}
142
143impl<T> Input for &[T]
144where
145    for<'a> &'a T: Project,
146{
147    fn resolve_arg(&mut self, expr_arg: &ExprArg, projection: &Projection) -> Option<Expr> {
148        (&self[expr_arg.position]).project(projection)
149    }
150}