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