Skip to content

Commit 3bf5eef

Browse files
committed
Add new comparison operators
1 parent dcf9e51 commit 3bf5eef

File tree

7 files changed

+362
-1
lines changed

7 files changed

+362
-1
lines changed

dsc/tests/dsc_expressions.tests.ps1

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,18 @@ resources:
112112
@{ expression = "[equals('a', 'a')]"; expected = $true }
113113
@{ expression = "[equals('a', 'b')]"; expected = $false }
114114
@{ expression = "[not(equals('a', 'b'))]"; expected = $true }
115+
@{ expression = "[greater(5, 3)]"; expected = $true }
116+
@{ expression = "[greater(3, 5)]"; expected = $false }
117+
@{ expression = "[greater(5, 5)]"; expected = $false }
118+
@{ expression = "[greaterOrEquals(5, 3)]"; expected = $true }
119+
@{ expression = "[greaterOrEquals(3, 5)]"; expected = $false }
120+
@{ expression = "[greaterOrEquals(5, 5)]"; expected = $true }
121+
@{ expression = "[less(3, 5)]"; expected = $true }
122+
@{ expression = "[less(5, 3)]"; expected = $false }
123+
@{ expression = "[less(5, 5)]"; expected = $false }
124+
@{ expression = "[lessOrEquals(3, 5)]"; expected = $true }
125+
@{ expression = "[lessOrEquals(5, 3)]"; expected = $false }
126+
@{ expression = "[lessOrEquals(5, 5)]"; expected = $true }
115127
@{ expression = "[and(true, true)]"; expected = $true }
116128
@{ expression = "[and(true, false)]"; expected = $false }
117129
@{ expression = "[or(false, true)]"; expected = $true }

dsc_lib/locales/en-us.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,14 @@ description = "Evaluates if the two values are the same"
248248
description = "Returns the boolean value false"
249249
invoked = "false function"
250250

251+
[functions.greater]
252+
description = "Evaluates if the first value is greater than the second value"
253+
invoked = "greater function"
254+
255+
[functions.greaterOrEquals]
256+
description = "Evaluates if the first value is greater than or equal to the second value"
257+
invoked = "greaterOrEquals function"
258+
251259
[functions.format]
252260
description = "Formats a string using the given arguments"
253261
experimental = "`format()` function is experimental"
@@ -267,6 +275,14 @@ parseStringError = "unable to parse string to int"
267275
castError = "unable to cast to int"
268276
parseNumError = "unable to parse number to int"
269277

278+
[functions.less]
279+
description = "Evaluates if the first value is less than the second value"
280+
invoked = "less function"
281+
282+
[functions.lessOrEquals]
283+
description = "Evaluates if the first value is less than or equal to the second value"
284+
invoked = "lessOrEquals function"
285+
270286
[functions.max]
271287
description = "Returns the largest number from a list of numbers"
272288
emptyArray = "Array cannot be empty"

dsc_lib/src/functions/greater.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
use crate::DscError;
5+
use crate::configure::context::Context;
6+
use crate::functions::{AcceptedArgKind, FunctionCategory};
7+
use rust_i18n::t;
8+
use super::Function;
9+
use serde_json::Value;
10+
11+
#[derive(Debug, Default)]
12+
pub struct Greater {}
13+
14+
impl Function for Greater {
15+
fn description(&self) -> String {
16+
t!("functions.greater.description").to_string()
17+
}
18+
19+
fn category(&self) -> FunctionCategory {
20+
FunctionCategory::Comparison
21+
}
22+
23+
fn accepted_arg_types(&self) -> Vec<AcceptedArgKind> {
24+
vec![AcceptedArgKind::Number, AcceptedArgKind::String]
25+
}
26+
27+
fn min_args(&self) -> usize {
28+
2
29+
}
30+
31+
fn max_args(&self) -> usize {
32+
2
33+
}
34+
35+
fn invoke(&self, args: &[Value], _context: &Context) -> Result<Value, DscError> {
36+
let first = &args[0];
37+
let second = &args[1];
38+
39+
if let (Some(num1), Some(num2)) = (first.as_f64(), second.as_f64()) {
40+
return Ok(Value::Bool(num1 > num2));
41+
}
42+
43+
if let (Some(str1), Some(str2)) = (first.as_str(), second.as_str()) {
44+
return Ok(Value::Bool(str1 > str2));
45+
}
46+
47+
Ok(Value::Bool(false))
48+
}
49+
}
50+
51+
#[cfg(test)]
52+
mod tests {
53+
use crate::configure::context::Context;
54+
use crate::parser::Statement;
55+
use serde_json::Value;
56+
57+
#[test]
58+
fn number_greater() {
59+
let mut parser = Statement::new().unwrap();
60+
let result = parser.parse_and_execute("[greater(2,1)]", &Context::new()).unwrap();
61+
assert_eq!(result, Value::Bool(true));
62+
}
63+
64+
#[test]
65+
fn number_not_greater() {
66+
let mut parser = Statement::new().unwrap();
67+
let result = parser.parse_and_execute("[greater(1,2)]", &Context::new()).unwrap();
68+
assert_eq!(result, Value::Bool(false));
69+
}
70+
71+
#[test]
72+
fn number_equal() {
73+
let mut parser = Statement::new().unwrap();
74+
let result = parser.parse_and_execute("[greater(1,1)]", &Context::new()).unwrap();
75+
assert_eq!(result, Value::Bool(false));
76+
}
77+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
use crate::DscError;
5+
use crate::configure::context::Context;
6+
use crate::functions::{AcceptedArgKind, Function, FunctionCategory};
7+
use rust_i18n::t;
8+
use serde_json::Value;
9+
use tracing::debug;
10+
11+
#[derive(Debug, Default)]
12+
pub struct GreaterOrEquals {}
13+
14+
impl Function for GreaterOrEquals {
15+
fn description(&self) -> String {
16+
t!("functions.greaterOrEquals.description").to_string()
17+
}
18+
19+
fn category(&self) -> FunctionCategory {
20+
FunctionCategory::Comparison
21+
}
22+
23+
fn min_args(&self) -> usize {
24+
2
25+
}
26+
27+
fn max_args(&self) -> usize {
28+
2
29+
}
30+
31+
fn accepted_arg_types(&self) -> Vec<AcceptedArgKind> {
32+
vec![AcceptedArgKind::Number, AcceptedArgKind::Number]
33+
}
34+
35+
fn invoke(&self, args: &[Value], _context: &Context) -> Result<Value, DscError> {
36+
debug!("{}", t!("functions.greaterOrEquals.invoked"));
37+
38+
let num1 = match &args[0] {
39+
Value::Number(n) => n.as_f64().ok_or_else(|| DscError::Parser(t!("functions.invalidArguments").to_string()))?,
40+
_ => return Err(DscError::Parser(t!("functions.invalidArguments").to_string())),
41+
};
42+
43+
let num2 = match &args[1] {
44+
Value::Number(n) => n.as_f64().ok_or_else(|| DscError::Parser(t!("functions.invalidArguments").to_string()))?,
45+
_ => return Err(DscError::Parser(t!("functions.invalidArguments").to_string())),
46+
};
47+
48+
Ok(Value::Bool(num1 >= num2))
49+
}
50+
}
51+
52+
#[cfg(test)]
53+
mod tests {
54+
use super::*;
55+
use crate::configure::context::Context;
56+
57+
#[test]
58+
fn test_greater_or_equals() {
59+
let greater_or_equals = GreaterOrEquals {};
60+
let context = Context::new().unwrap();
61+
62+
let result = greater_or_equals.invoke(&[Value::Number(5.into()), Value::Number(3.into())], &context);
63+
assert!(result.is_ok());
64+
assert_eq!(result.unwrap(), Value::Bool(true));
65+
66+
let result = greater_or_equals.invoke(&[Value::Number(3.into()), Value::Number(5.into())], &context);
67+
assert!(result.is_ok());
68+
assert_eq!(result.unwrap(), Value::Bool(false));
69+
70+
let result = greater_or_equals.invoke(&[Value::Number(5.into()), Value::Number(5.into())], &context);
71+
assert!(result.is_ok());
72+
assert_eq!(result.unwrap(), Value::Bool(true));
73+
}
74+
75+
#[test]
76+
fn test_invalid_args() {
77+
let greater_or_equals = GreaterOrEquals {};
78+
let context = Context::new().unwrap();
79+
80+
let result = greater_or_equals.invoke(&[Value::Number(5.into())], &context);
81+
assert!(result.is_err());
82+
}
83+
}

dsc_lib/src/functions/less.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
use crate::DscError;
5+
use crate::configure::context::Context;
6+
use crate::functions::{AcceptedArgKind, Function, FunctionCategory};
7+
use rust_i18n::t;
8+
use serde_json::Value;
9+
use tracing::debug;
10+
11+
#[derive(Debug, Default)]
12+
pub struct Less {}
13+
14+
impl Function for Less {
15+
fn description(&self) -> String {
16+
t!("functions.less.description").to_string()
17+
}
18+
19+
fn category(&self) -> FunctionCategory {
20+
FunctionCategory::Comparison
21+
}
22+
23+
fn min_args(&self) -> usize {
24+
2
25+
}
26+
27+
fn max_args(&self) -> usize {
28+
2
29+
}
30+
31+
fn accepted_arg_types(&self) -> Vec<AcceptedArgKind> {
32+
vec![AcceptedArgKind::Number, AcceptedArgKind::Number]
33+
}
34+
35+
fn invoke(&self, args: &[Value], _context: &Context) -> Result<Value, DscError> {
36+
debug!("{}", t!("functions.less.invoked"));
37+
38+
let num1 = match &args[0] {
39+
Value::Number(n) => n.as_f64().ok_or_else(|| DscError::Parser(t!("functions.invalidArguments").to_string()))?,
40+
_ => return Err(DscError::Parser(t!("functions.invalidArguments").to_string())),
41+
};
42+
43+
let num2 = match &args[1] {
44+
Value::Number(n) => n.as_f64().ok_or_else(|| DscError::Parser(t!("functions.invalidArguments").to_string()))?,
45+
_ => return Err(DscError::Parser(t!("functions.invalidArguments").to_string())),
46+
};
47+
48+
Ok(Value::Bool(num1 < num2))
49+
}
50+
}
51+
52+
#[cfg(test)]
53+
mod tests {
54+
use super::*;
55+
use crate::configure::context::Context;
56+
57+
#[test]
58+
fn test_less() {
59+
let less = Less {};
60+
let context = Context::new().unwrap();
61+
62+
let result = less.invoke(&[Value::Number(3.into()), Value::Number(5.into())], &context);
63+
assert!(result.is_ok());
64+
assert_eq!(result.unwrap(), Value::Bool(true));
65+
66+
let result = less.invoke(&[Value::Number(5.into()), Value::Number(3.into())], &context);
67+
assert!(result.is_ok());
68+
assert_eq!(result.unwrap(), Value::Bool(false));
69+
70+
let result = less.invoke(&[Value::Number(5.into()), Value::Number(5.into())], &context);
71+
assert!(result.is_ok());
72+
assert_eq!(result.unwrap(), Value::Bool(false));
73+
}
74+
75+
#[test]
76+
fn test_invalid_args() {
77+
let less = Less {};
78+
let context = Context::new().unwrap();
79+
80+
let result = less.invoke(&[Value::Number(5.into())], &context);
81+
assert!(result.is_err());
82+
}
83+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
use crate::DscError;
5+
use crate::configure::context::Context;
6+
use crate::functions::{AcceptedArgKind, Function, FunctionCategory};
7+
use rust_i18n::t;
8+
use serde_json::Value;
9+
use tracing::debug;
10+
11+
#[derive(Debug, Default)]
12+
pub struct LessOrEquals {}
13+
14+
impl Function for LessOrEquals {
15+
fn description(&self) -> String {
16+
t!("functions.lessOrEquals.description").to_string()
17+
}
18+
19+
fn category(&self) -> FunctionCategory {
20+
FunctionCategory::Comparison
21+
}
22+
23+
fn min_args(&self) -> usize {
24+
2
25+
}
26+
27+
fn max_args(&self) -> usize {
28+
2
29+
}
30+
31+
fn accepted_arg_types(&self) -> Vec<AcceptedArgKind> {
32+
vec![AcceptedArgKind::Number, AcceptedArgKind::Number]
33+
}
34+
35+
fn invoke(&self, args: &[Value], _context: &Context) -> Result<Value, DscError> {
36+
debug!("{}", t!("functions.lessOrEquals.invoked"));
37+
38+
let num1 = match &args[0] {
39+
Value::Number(n) => n.as_f64().ok_or_else(|| DscError::Parser(t!("functions.invalidArguments").to_string()))?,
40+
_ => return Err(DscError::Parser(t!("functions.invalidArguments").to_string())),
41+
};
42+
43+
let num2 = match &args[1] {
44+
Value::Number(n) => n.as_f64().ok_or_else(|| DscError::Parser(t!("functions.invalidArguments").to_string()))?,
45+
_ => return Err(DscError::Parser(t!("functions.invalidArguments").to_string())),
46+
};
47+
48+
Ok(Value::Bool(num1 <= num2))
49+
}
50+
}
51+
52+
#[cfg(test)]
53+
mod tests {
54+
use super::*;
55+
use crate::configure::context::Context;
56+
57+
#[test]
58+
fn test_less_or_equals() {
59+
let less_or_equals = LessOrEquals {};
60+
let context = Context::new().unwrap();
61+
62+
let result = less_or_equals.invoke(&[Value::Number(3.into()), Value::Number(5.into())], &context);
63+
assert!(result.is_ok());
64+
assert_eq!(result.unwrap(), Value::Bool(true));
65+
66+
let result = less_or_equals.invoke(&[Value::Number(5.into()), Value::Number(3.into())], &context);
67+
assert!(result.is_ok());
68+
assert_eq!(result.unwrap(), Value::Bool(false));
69+
70+
let result = less_or_equals.invoke(&[Value::Number(5.into()), Value::Number(5.into())], &context);
71+
assert!(result.is_ok());
72+
assert_eq!(result.unwrap(), Value::Bool(true));
73+
}
74+
75+
#[test]
76+
fn test_invalid_args() {
77+
let less_or_equals = LessOrEquals {};
78+
let context = Context::new().unwrap();
79+
80+
let result = less_or_equals.invoke(&[Value::Number(5.into())], &context);
81+
assert!(result.is_err());
82+
}
83+
}

0 commit comments

Comments
 (0)