Skip to content

Commit 032bba2

Browse files
committed
chore(stdlib): Resolve bool and int constants during compilation
1 parent 9bbe02a commit 032bba2

16 files changed

Lines changed: 281 additions & 166 deletions

File tree

benches/stdlib.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ criterion_group!(
2323
community_id,
2424
compact,
2525
contains,
26+
contains_all,
2627
crc,
2728
decode_base16,
2829
decode_base64,
@@ -298,6 +299,20 @@ bench_function! {
298299
}
299300
}
300301

302+
bench_function! {
303+
contains_all => vrl::stdlib::ContainsAll;
304+
305+
case_sensitive {
306+
args: func_args![value: "abcdefg", substrings: value!(["cde"]), case_sensitive: true],
307+
want: Ok(value!(true)),
308+
}
309+
310+
case_insensitive {
311+
args: func_args![value: "abcdefg", substrings: value!(["CDE"]), case_sensitive: false],
312+
want: Ok(value!(true)),
313+
}
314+
}
315+
301316
bench_function! {
302317
crc => vrl::stdlib::Crc;
303318

src/compiler/value/error.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,9 @@ impl From<ValueError> for ExpressionError {
9999
}
100100
}
101101
}
102+
103+
impl From<ValueError> for Box<dyn crate::diagnostic::DiagnosticMessage> {
104+
fn from(error: ValueError) -> Self {
105+
Box::new(error) as _
106+
}
107+
}

src/stdlib/chunks.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::compiler::prelude::*;
2+
use crate::stdlib::util::IntegerConstOrExpr;
23

3-
fn chunks(value: Value, chunk_size: Value) -> Resolved {
4+
fn chunks(value: Value, chunk_size: i64) -> Resolved {
45
let bytes = value.try_bytes()?;
5-
let chunk_size = chunk_size.try_integer()?;
66

77
if chunk_size < 1 {
88
return Err(r#""chunk_size" must be at least 1 byte"#.into());
@@ -86,17 +86,15 @@ impl Function for Chunks {
8686
arguments: ArgumentList,
8787
) -> Compiled {
8888
let value = arguments.required("value");
89-
let chunk_size = arguments.required("chunk_size");
89+
let chunk_size = IntegerConstOrExpr::new(arguments.required("chunk_size"), state)?;
9090

9191
// chunk_size is converted to a usize, so if a user-supplied Value::Integer (i64) is
9292
// larger than the platform's usize::MAX, it could fail to convert.
93-
if let Some(literal) = chunk_size.resolve_constant(state)
94-
&& let Some(integer) = literal.as_integer()
95-
{
93+
if let IntegerConstOrExpr::Constant(integer) = chunk_size {
9694
if integer < 1 {
9795
return Err(function::Error::InvalidArgument {
9896
keyword: "chunk_size",
99-
value: literal,
97+
value: Value::Integer(integer),
10098
error: r#""chunk_size" must be at least 1 byte"#,
10199
}
102100
.into());
@@ -105,7 +103,7 @@ impl Function for Chunks {
105103
if usize::try_from(integer).is_err() {
106104
return Err(function::Error::InvalidArgument {
107105
keyword: "chunk_size",
108-
value: literal,
106+
value: Value::Integer(integer),
109107
error: r#""chunk_size" is too large"#,
110108
}
111109
.into());
@@ -119,7 +117,7 @@ impl Function for Chunks {
119117
#[derive(Debug, Clone)]
120118
struct ChunksFn {
121119
value: Box<dyn Expression>,
122-
chunk_size: Box<dyn Expression>,
120+
chunk_size: IntegerConstOrExpr,
123121
}
124122

125123
impl FunctionExpression for ChunksFn {
@@ -130,8 +128,8 @@ impl FunctionExpression for ChunksFn {
130128
chunks(value, chunk_size)
131129
}
132130

133-
fn type_def(&self, state: &TypeState) -> TypeDef {
134-
let not_literal = self.chunk_size.resolve_constant(state).is_none();
131+
fn type_def(&self, _state: &TypeState) -> TypeDef {
132+
let not_literal = matches!(self.chunk_size, IntegerConstOrExpr::Expression(_));
135133

136134
TypeDef::array(Collection::from_unknown(Kind::bytes())).maybe_fallible(not_literal)
137135
}

src/stdlib/compact.rs

Lines changed: 52 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use super::util;
22
use crate::compiler::prelude::*;
3+
use crate::stdlib::util::BoolConstOrExpr;
34

45
static DEFAULT_RECURSIVE: Value = Value::Boolean(true);
56
static DEFAULT_NULL: Value = Value::Boolean(true);
@@ -29,21 +30,21 @@ const PARAMETERS: &[Parameter] = &[
2930
];
3031

3132
fn compact(
32-
recursive: Value,
33-
null: Value,
34-
string: Value,
35-
object: Value,
36-
array: Value,
37-
nullish: Value,
33+
recursive: bool,
34+
null: bool,
35+
string: bool,
36+
object: bool,
37+
array: bool,
38+
nullish: bool,
3839
value: Value,
3940
) -> Resolved {
4041
let options = CompactOptions {
41-
recursive: recursive.try_boolean()?,
42-
null: null.try_boolean()?,
43-
string: string.try_boolean()?,
44-
object: object.try_boolean()?,
45-
array: array.try_boolean()?,
46-
nullish: nullish.try_boolean()?,
42+
recursive,
43+
null,
44+
string,
45+
object,
46+
array,
47+
nullish,
4748
};
4849

4950
match value {
@@ -117,17 +118,35 @@ impl Function for Compact {
117118

118119
fn compile(
119120
&self,
120-
_state: &state::TypeState,
121+
state: &state::TypeState,
121122
_ctx: &mut FunctionCompileContext,
122123
arguments: ArgumentList,
123124
) -> Compiled {
124125
let value = arguments.required("value");
125-
let recursive = arguments.optional("recursive");
126-
let null = arguments.optional("null");
127-
let string = arguments.optional("string");
128-
let object = arguments.optional("object");
129-
let array = arguments.optional("array");
130-
let nullish = arguments.optional("nullish");
126+
let recursive = BoolConstOrExpr::new_with_default(
127+
arguments.optional("recursive"),
128+
state,
129+
&DEFAULT_RECURSIVE,
130+
)?;
131+
let null =
132+
BoolConstOrExpr::new_with_default(arguments.optional("null"), state, &DEFAULT_NULL)?;
133+
let string = BoolConstOrExpr::new_with_default(
134+
arguments.optional("string"),
135+
state,
136+
&DEFAULT_STRING,
137+
)?;
138+
let object = BoolConstOrExpr::new_with_default(
139+
arguments.optional("object"),
140+
state,
141+
&DEFAULT_OBJECT,
142+
)?;
143+
let array =
144+
BoolConstOrExpr::new_with_default(arguments.optional("array"), state, &DEFAULT_ARRAY)?;
145+
let nullish = BoolConstOrExpr::new_with_default(
146+
arguments.optional("nullish"),
147+
state,
148+
&DEFAULT_NULLISH,
149+
)?;
131150

132151
Ok(CompactFn {
133152
value,
@@ -145,12 +164,12 @@ impl Function for Compact {
145164
#[derive(Debug, Clone)]
146165
struct CompactFn {
147166
value: Box<dyn Expression>,
148-
recursive: Option<Box<dyn Expression>>,
149-
null: Option<Box<dyn Expression>>,
150-
string: Option<Box<dyn Expression>>,
151-
object: Option<Box<dyn Expression>>,
152-
array: Option<Box<dyn Expression>>,
153-
nullish: Option<Box<dyn Expression>>,
167+
recursive: BoolConstOrExpr,
168+
null: BoolConstOrExpr,
169+
string: BoolConstOrExpr,
170+
object: BoolConstOrExpr,
171+
array: BoolConstOrExpr,
172+
nullish: BoolConstOrExpr,
154173
}
155174

156175
#[derive(Debug)]
@@ -196,24 +215,12 @@ impl CompactOptions {
196215

197216
impl FunctionExpression for CompactFn {
198217
fn resolve(&self, ctx: &mut Context) -> Resolved {
199-
let recursive = self
200-
.recursive
201-
.map_resolve_with_default(ctx, || DEFAULT_RECURSIVE.clone())?;
202-
let null = self
203-
.null
204-
.map_resolve_with_default(ctx, || DEFAULT_NULL.clone())?;
205-
let string = self
206-
.string
207-
.map_resolve_with_default(ctx, || DEFAULT_STRING.clone())?;
208-
let object = self
209-
.object
210-
.map_resolve_with_default(ctx, || DEFAULT_OBJECT.clone())?;
211-
let array = self
212-
.array
213-
.map_resolve_with_default(ctx, || DEFAULT_ARRAY.clone())?;
214-
let nullish = self
215-
.nullish
216-
.map_resolve_with_default(ctx, || DEFAULT_NULLISH.clone())?;
218+
let recursive = self.recursive.resolve(ctx)?;
219+
let null = self.null.resolve(ctx)?;
220+
let string = self.string.resolve(ctx)?;
221+
let object = self.object.resolve(ctx)?;
222+
let array = self.array.resolve(ctx)?;
223+
let nullish = self.nullish.resolve(ctx)?;
217224
let value = self.value.resolve(ctx)?;
218225

219226
compact(recursive, null, string, object, array, nullish, value)
@@ -272,7 +279,7 @@ mod test {
272279

273280
#[test]
274281
fn test_compacted_array() {
275-
let cases = vec![
282+
let cases = [
276283
(
277284
vec!["".into(), "".into()], // expected
278285
vec!["".into(), Value::Null, "".into()], // original
@@ -333,7 +340,7 @@ mod test {
333340
#[test]
334341
#[allow(clippy::too_many_lines)]
335342
fn test_compacted_map() {
336-
let cases = vec![
343+
let cases = [
337344
(
338345
btreemap! {
339346
"key1" => "",

src/stdlib/contains.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::compiler::prelude::*;
22
use crate::stdlib::string_utils::convert_to_string;
3+
use crate::stdlib::util::BoolConstOrExpr;
34

45
static DEFAULT_CASE_SENSITIVE: Value = Value::Boolean(true);
56

@@ -18,8 +19,7 @@ const PARAMETERS: &[Parameter] = &[
1819
.default(&DEFAULT_CASE_SENSITIVE),
1920
];
2021

21-
fn contains(value: &Value, substring: &Value, case_sensitive: Value) -> Resolved {
22-
let case_sensitive = case_sensitive.try_boolean()?;
22+
fn contains(value: &Value, substring: &Value, case_sensitive: bool) -> Resolved {
2323
let value = convert_to_string(value, !case_sensitive)?;
2424
let substring = convert_to_string(substring, !case_sensitive)?;
2525
Ok(value.contains(substring.as_ref()).into())
@@ -51,13 +51,17 @@ impl Function for Contains {
5151

5252
fn compile(
5353
&self,
54-
_state: &state::TypeState,
54+
state: &state::TypeState,
5555
_ctx: &mut FunctionCompileContext,
5656
arguments: ArgumentList,
5757
) -> Compiled {
5858
let value = arguments.required("value");
5959
let substring = arguments.required("substring");
60-
let case_sensitive = arguments.optional("case_sensitive");
60+
let case_sensitive = BoolConstOrExpr::new_with_default(
61+
arguments.optional("case_sensitive"),
62+
state,
63+
&DEFAULT_CASE_SENSITIVE,
64+
)?;
6165

6266
Ok(ContainsFn {
6367
value,
@@ -87,16 +91,14 @@ impl Function for Contains {
8791
struct ContainsFn {
8892
value: Box<dyn Expression>,
8993
substring: Box<dyn Expression>,
90-
case_sensitive: Option<Box<dyn Expression>>,
94+
case_sensitive: BoolConstOrExpr,
9195
}
9296

9397
impl FunctionExpression for ContainsFn {
9498
fn resolve(&self, ctx: &mut Context) -> Resolved {
9599
let value = self.value.resolve(ctx)?;
96100
let substring = self.substring.resolve(ctx)?;
97-
let case_sensitive = self
98-
.case_sensitive
99-
.map_resolve_with_default(ctx, || DEFAULT_CASE_SENSITIVE.clone())?;
101+
let case_sensitive = self.case_sensitive.resolve(ctx)?;
100102

101103
contains(&value, &substring, case_sensitive)
102104
}
@@ -190,5 +192,14 @@ mod tests {
190192
want: Ok(value!(true)),
191193
tdef: TypeDef::boolean().infallible(),
192194
}
195+
196+
case_insensitive_invalid_type {
197+
args: func_args![value: value!("foobar"),
198+
substring: value!("BAR"),
199+
case_sensitive: value!("no")
200+
],
201+
want: Err("expected boolean, got string"),
202+
tdef: TypeDef::boolean().infallible(),
203+
}
193204
];
194205
}

src/stdlib/contains_all.rs

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
use crate::compiler::prelude::*;
22
use crate::stdlib::string_utils::convert_to_string;
3+
use crate::stdlib::util::BoolConstOrExpr;
34

4-
fn contains_all(value: &Value, substrings: Value, case_sensitive: Option<Value>) -> Resolved {
5-
let case_sensitive = match case_sensitive {
6-
Some(v) => v.try_boolean()?,
7-
None => true,
8-
};
9-
5+
fn contains_all(value: &Value, substrings: Value, case_sensitive: bool) -> Resolved {
106
let value_string = convert_to_string(value, !case_sensitive)?;
117
let substring_values = substrings.try_array()?;
128

@@ -58,13 +54,17 @@ impl Function for ContainsAll {
5854

5955
fn compile(
6056
&self,
61-
_state: &state::TypeState,
57+
state: &state::TypeState,
6258
_ctx: &mut FunctionCompileContext,
6359
arguments: ArgumentList,
6460
) -> Compiled {
6561
let value = arguments.required("value");
6662
let substrings = arguments.required("substrings");
67-
let case_sensitive = arguments.optional("case_sensitive");
63+
let case_sensitive = BoolConstOrExpr::new_with_default(
64+
arguments.optional("case_sensitive"),
65+
state,
66+
&Value::Boolean(true),
67+
)?;
6868

6969
Ok(ContainsAllFn {
7070
value,
@@ -99,18 +99,14 @@ impl Function for ContainsAll {
9999
struct ContainsAllFn {
100100
value: Box<dyn Expression>,
101101
substrings: Box<dyn Expression>,
102-
case_sensitive: Option<Box<dyn Expression>>,
102+
case_sensitive: BoolConstOrExpr,
103103
}
104104

105105
impl FunctionExpression for ContainsAllFn {
106106
fn resolve(&self, ctx: &mut Context) -> Resolved {
107107
let value = self.value.resolve(ctx)?;
108108
let substrings = self.substrings.resolve(ctx)?;
109-
let case_sensitive = self
110-
.case_sensitive
111-
.as_ref()
112-
.map(|expr| expr.resolve(ctx))
113-
.transpose()?;
109+
let case_sensitive = self.case_sensitive.resolve(ctx)?;
114110
contains_all(&value, substrings, case_sensitive)
115111
}
116112

0 commit comments

Comments
 (0)