From 8d964ade3b0cfb4b317b9742cde6386a18accb0a Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Wed, 6 Mar 2024 11:26:34 -0500 Subject: [PATCH 1/2] add div function --- dsc_lib/src/functions/div.rs | 77 ++++++++++++++++++++++++++++++++++++ dsc_lib/src/functions/mod.rs | 2 + 2 files changed, 79 insertions(+) create mode 100644 dsc_lib/src/functions/div.rs diff --git a/dsc_lib/src/functions/div.rs b/dsc_lib/src/functions/div.rs new file mode 100644 index 000000000..263adb994 --- /dev/null +++ b/dsc_lib/src/functions/div.rs @@ -0,0 +1,77 @@ +// 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 Div {} + +impl Function for Div { + fn min_args(&self) -> usize { + 2 + } + + fn max_args(&self) -> usize { + 2 + } + + fn accepted_arg_types(&self) -> Vec { + vec![AcceptedArgKind::Number] + } + + fn invoke(&self, args: &[Value], _context: &Context) -> Result { + debug!("div function"); + if let Some(value) = args[0].as_i64().unwrap().checked_div(args[1].as_i64().unwrap()) { + Ok(Value::Number(value.into())) + } else { + Err(DscError::Parser("Cannot divide by zero".to_string())) + } + } +} + +#[cfg(test)] +mod tests { + use crate::configure::context::Context; + use crate::parser::Statement; + + #[test] + fn numbers() { + let mut parser = Statement::new().unwrap(); + let result = parser.parse_and_execute("[div(8, 3)]", &Context::new()).unwrap(); + assert_eq!(result, 2); + } + + #[test] + fn nested() { + let mut parser = Statement::new().unwrap(); + let result = parser.parse_and_execute("[div(18, div(9, 3))]", &Context::new()).unwrap(); + assert_eq!(result, 6); + } + + #[test] + fn invalid_one_parameter() { + let mut parser = Statement::new().unwrap(); + let result = parser.parse_and_execute("[div(5)]", &Context::new()); + assert!(result.is_err()); + } + + #[test] + fn invalid_div_by_zero() { + let mut parser = Statement::new().unwrap(); + let result = parser.parse_and_execute("[div(5, 0)]", &Context::new()); + assert!(result.is_err()); + } + + #[test] + fn overflow_input() { + let mut parser = Statement::new().unwrap(); + // max value for i64 is 2^63 -1 (or 9,223,372,036,854,775,807) + let result = parser.parse_and_execute("[div(9223372036854775808, 2)]", &Context::new()); + assert!(result.is_err()); + } +} + diff --git a/dsc_lib/src/functions/mod.rs b/dsc_lib/src/functions/mod.rs index 400b82931..153e322aa 100644 --- a/dsc_lib/src/functions/mod.rs +++ b/dsc_lib/src/functions/mod.rs @@ -10,6 +10,7 @@ use serde_json::Value; pub mod base64; pub mod concat; pub mod create_array; +pub mod div; pub mod envvar; pub mod parameters; pub mod resource_id; @@ -57,6 +58,7 @@ impl FunctionDispatcher { functions.insert("base64".to_string(), Box::new(base64::Base64{})); functions.insert("concat".to_string(), Box::new(concat::Concat{})); 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("parameters".to_string(), Box::new(parameters::Parameters{})); functions.insert("resourceId".to_string(), Box::new(resource_id::ResourceId{})); From a43d298dfb9b4c02ec36b0768d60871ed673794f Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Wed, 6 Mar 2024 15:58:36 -0500 Subject: [PATCH 2/2] change unwrap call --- dsc_lib/src/functions/div.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/dsc_lib/src/functions/div.rs b/dsc_lib/src/functions/div.rs index 263adb994..04516809a 100644 --- a/dsc_lib/src/functions/div.rs +++ b/dsc_lib/src/functions/div.rs @@ -25,10 +25,14 @@ impl Function for Div { fn invoke(&self, args: &[Value], _context: &Context) -> Result { debug!("div function"); - if let Some(value) = args[0].as_i64().unwrap().checked_div(args[1].as_i64().unwrap()) { - Ok(Value::Number(value.into())) + if let (Some(arg1), Some(arg2)) = (args[0].as_i64(), args[1].as_i64()) { + if let Some(value) = arg1.checked_div(arg2) { + Ok(Value::Number(value.into())) + } else { + Err(DscError::Parser("Cannot divide by zero".to_string())) + } } else { - Err(DscError::Parser("Cannot divide by zero".to_string())) + Err(DscError::Parser("Invalid argument(s)".to_string())) } } }