From 61652b76967c9963b8a324a17bb5c179849e8137 Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Fri, 8 Mar 2024 15:43:30 -0500 Subject: [PATCH 1/5] add min function --- dsc_lib/src/functions/min.rs | 136 +++++++++++++++++++++++++++++++++++ dsc_lib/src/functions/mod.rs | 2 + 2 files changed, 138 insertions(+) create mode 100644 dsc_lib/src/functions/min.rs diff --git a/dsc_lib/src/functions/min.rs b/dsc_lib/src/functions/min.rs new file mode 100644 index 00000000..40d5c17a --- /dev/null +++ b/dsc_lib/src/functions/min.rs @@ -0,0 +1,136 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::DscError; +use crate::configure::context::Context; +use crate::functions::{AcceptedArgKind, Function}; +use serde_json::Value; +use tracing::debug; + +#[derive(Debug, Default)] +pub struct Min {} + +impl Function for Min { + fn min_args(&self) -> usize { + 1 + } + + fn max_args(&self) -> usize { + usize::MAX + } + + fn accepted_arg_types(&self) -> Vec { + vec![AcceptedArgKind::Number, AcceptedArgKind::Array] + } + + fn invoke(&self, args: &[Value], _context: &Context) -> Result { + debug!("min function"); + if args.len() == 1 { + if let Some(array) = args[0].as_array() { + if array.len() > 1 { + find_min(array) + } + else { + Err(DscError::Parser("Array must contain more than 1 integer".to_string())) + } + } + else { + Err(DscError::Parser("List must contain more than 1 integer".to_string())) + } + } + else { + find_min(args) + } + } +} + +fn find_min(args: &[Value]) -> Result { + let mut min_value = i64::MAX; + for value in args { + if let Some(int_value) = value.as_i64() { + if int_value < min_value { + min_value = int_value; + } + } + else { + return Err(DscError::Parser("Input must only contain integers".to_string())); + } + } + Ok(Value::Number(min_value.into())) +} + +#[cfg(test)] +mod tests { + use crate::configure::context::Context; + use crate::parser::Statement; + + #[test] + fn list() { + let mut parser = Statement::new().unwrap(); + let result = parser.parse_and_execute("[min(3,2,5,4)]", &Context::new()).unwrap(); + assert_eq!(result, 2); + } + + #[test] + fn list_with_spaces() { + let mut parser = Statement::new().unwrap(); + let result = parser.parse_and_execute("[min(3, 2, 5, 4)]", &Context::new()).unwrap(); + assert_eq!(result, 2); + } + + #[test] + fn array() { + let mut parser = Statement::new().unwrap(); + let result = parser.parse_and_execute("[min(createArray(0, 3, 2, 5, 4)]", &Context::new()).unwrap(); + assert_eq!(result, 0); + } + + #[test] + fn array_single_value() { + let mut parser = Statement::new().unwrap(); + let result = parser.parse_and_execute("[min(createArray(0)]", &Context::new()); + assert!(result.is_err()); + } + + #[test] + fn arrays() { + let mut parser = Statement::new().unwrap(); + let result = parser.parse_and_execute("[min(createArray('0','3'), createArray('2','5'))]", &Context::new()); + assert!(result.is_err()); + } + + #[test] + fn string_and_numbers() { + let mut parser = Statement::new().unwrap(); + let result = parser.parse_and_execute("[min('a', 1)]", &Context::new()); + assert!(result.is_err()); + } + + #[test] + fn nested() { + let mut parser = Statement::new().unwrap(); + let result = parser.parse_and_execute("[min(8, min(2, 5), 3)]", &Context::new()).unwrap(); + assert_eq!(result, 2); + } + + #[test] + fn invalid_one_parameter() { + let mut parser = Statement::new().unwrap(); + let result = parser.parse_and_execute("[min(1)]", &Context::new()); + assert!(result.is_err()); + } + + #[test] + fn int_and_array() { + let mut parser = Statement::new().unwrap(); + let result = parser.parse_and_execute("[min(1, createArray(0,2))]", &Context::new()); + assert!(result.is_err()); + } + + #[test] + fn array_and_int() { + let mut parser = Statement::new().unwrap(); + let result = parser.parse_and_execute("[min(createArray(0,2), 1)]", &Context::new()); + assert!(result.is_err()); + } +} diff --git a/dsc_lib/src/functions/mod.rs b/dsc_lib/src/functions/mod.rs index 28eddc4b..2301bd85 100644 --- a/dsc_lib/src/functions/mod.rs +++ b/dsc_lib/src/functions/mod.rs @@ -13,6 +13,7 @@ pub mod concat; pub mod create_array; pub mod div; pub mod envvar; +pub mod min; pub mod mul; pub mod parameters; pub mod resource_id; @@ -64,6 +65,7 @@ impl FunctionDispatcher { functions.insert("createArray".to_string(), Box::new(create_array::CreateArray{})); functions.insert("div".to_string(), Box::new(div::Div{})); functions.insert("envvar".to_string(), Box::new(envvar::Envvar{})); + functions.insert("min".to_string(), Box::new(min::Min{})); functions.insert("mul".to_string(), Box::new(mul::Mul{})); functions.insert("parameters".to_string(), Box::new(parameters::Parameters{})); functions.insert("resourceId".to_string(), Box::new(resource_id::ResourceId{})); From 944eafb8a3958554da5228a38de7856dd0a492fa Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Mon, 11 Mar 2024 12:20:31 -0400 Subject: [PATCH 2/5] address review feedback --- dsc_lib/src/functions/min.rs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/dsc_lib/src/functions/min.rs b/dsc_lib/src/functions/min.rs index 40d5c17a..43e29a4c 100644 --- a/dsc_lib/src/functions/min.rs +++ b/dsc_lib/src/functions/min.rs @@ -35,7 +35,7 @@ impl Function for Min { } } else { - Err(DscError::Parser("List must contain more than 1 integer".to_string())) + Err(DscError::Parser("Array cannot be empty".to_string())) } } else { @@ -45,18 +45,9 @@ impl Function for Min { } fn find_min(args: &[Value]) -> Result { - let mut min_value = i64::MAX; - for value in args { - if let Some(int_value) = value.as_i64() { - if int_value < min_value { - min_value = int_value; - } - } - else { - return Err(DscError::Parser("Input must only contain integers".to_string())); - } - } - Ok(Value::Number(min_value.into())) + let array = args.into_iter().map(|v| v.as_i64().ok_or(DscError::Parser("Input must only contain integers".to_string()))).collect::, DscError>>()?; + let value = array.iter().min().ok_or(DscError::Parser("Unable to find min value".to_string()))?; + Ok(Value::Number(value.clone().into())) } #[cfg(test)] @@ -116,7 +107,7 @@ mod tests { #[test] fn invalid_one_parameter() { let mut parser = Statement::new().unwrap(); - let result = parser.parse_and_execute("[min(1)]", &Context::new()); + let result = parser.parse_and_execute("[min(createArray(1))]", &Context::new()); assert!(result.is_err()); } From d77f594420a27c9ccc49787b5f4f115c0f4c18b5 Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Mon, 11 Mar 2024 12:26:36 -0400 Subject: [PATCH 3/5] fix clippy --- dsc_lib/src/functions/min.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dsc_lib/src/functions/min.rs b/dsc_lib/src/functions/min.rs index 43e29a4c..1173c5b7 100644 --- a/dsc_lib/src/functions/min.rs +++ b/dsc_lib/src/functions/min.rs @@ -45,9 +45,9 @@ impl Function for Min { } fn find_min(args: &[Value]) -> Result { - let array = args.into_iter().map(|v| v.as_i64().ok_or(DscError::Parser("Input must only contain integers".to_string()))).collect::, DscError>>()?; + let array = args.iter().map(|v| v.as_i64().ok_or(DscError::Parser("Input must only contain integers".to_string()))).collect::, DscError>>()?; let value = array.iter().min().ok_or(DscError::Parser("Unable to find min value".to_string()))?; - Ok(Value::Number(value.clone().into())) + Ok(Value::Number((*value).into())) } #[cfg(test)] From 699e1d50b36e894d8356f90dd261bb0097db0563 Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Tue, 12 Mar 2024 12:39:31 -0300 Subject: [PATCH 4/5] fix tests --- dsc_lib/src/functions/min.rs | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/dsc_lib/src/functions/min.rs b/dsc_lib/src/functions/min.rs index 1173c5b7..916196a3 100644 --- a/dsc_lib/src/functions/min.rs +++ b/dsc_lib/src/functions/min.rs @@ -27,12 +27,7 @@ impl Function for Min { debug!("min function"); if args.len() == 1 { if let Some(array) = args[0].as_array() { - if array.len() > 1 { - find_min(array) - } - else { - Err(DscError::Parser("Array must contain more than 1 integer".to_string())) - } + find_min(array) } else { Err(DscError::Parser("Array cannot be empty".to_string())) @@ -79,14 +74,14 @@ mod tests { #[test] fn array_single_value() { let mut parser = Statement::new().unwrap(); - let result = parser.parse_and_execute("[min(createArray(0)]", &Context::new()); - assert!(result.is_err()); + let result = parser.parse_and_execute("[min(createArray(0)]", &Context::new()).unwrap(); + assert_eq!(result, 0); } #[test] fn arrays() { let mut parser = Statement::new().unwrap(); - let result = parser.parse_and_execute("[min(createArray('0','3'), createArray('2','5'))]", &Context::new()); + let result = parser.parse_and_execute("[min(createArray(0, 3), createArray(2, 5))]", &Context::new()); assert!(result.is_err()); } @@ -104,13 +99,6 @@ mod tests { assert_eq!(result, 2); } - #[test] - fn invalid_one_parameter() { - let mut parser = Statement::new().unwrap(); - let result = parser.parse_and_execute("[min(createArray(1))]", &Context::new()); - assert!(result.is_err()); - } - #[test] fn int_and_array() { let mut parser = Statement::new().unwrap(); From 1e572f5dbb62803e6bb5cbec489ec1e0e6349946 Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Wed, 13 Mar 2024 13:22:49 -0300 Subject: [PATCH 5/5] test negative int --- dsc_lib/src/functions/min.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dsc_lib/src/functions/min.rs b/dsc_lib/src/functions/min.rs index 916196a3..8fe9444c 100644 --- a/dsc_lib/src/functions/min.rs +++ b/dsc_lib/src/functions/min.rs @@ -95,8 +95,8 @@ mod tests { #[test] fn nested() { let mut parser = Statement::new().unwrap(); - let result = parser.parse_and_execute("[min(8, min(2, 5), 3)]", &Context::new()).unwrap(); - assert_eq!(result, 2); + let result = parser.parse_and_execute("[min(8, min(2, -9), 3)]", &Context::new()).unwrap(); + assert_eq!(result, -9); } #[test]