diff --git a/Cargo.toml b/Cargo.toml index e8723451..ad8eb28e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] anyhow = "1.0.66" +semver = "1.0.20" ordered-float = "3.4.0" serde = {version = "1.0.150", features = ["derive", "rc"] } serde_json = "1.0.89" diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs index 927a46b0..da81bbe3 100644 --- a/src/builtins/mod.rs +++ b/src/builtins/mod.rs @@ -11,6 +11,7 @@ pub mod deprecated; mod encoding; pub mod numbers; mod objects; +mod semver; pub mod sets; mod strings; mod time; @@ -59,7 +60,7 @@ lazy_static! { //http::register(&mut m); //net::register(&mut m); //uuid::register(&mut m); - //semantic_versions::register(&mut m); + semver::register(&mut m); //rego::register(&mut m); //opa::register(&mut m); debugging::register(&mut m); diff --git a/src/builtins/semver.rs b/src/builtins/semver.rs new file mode 100644 index 00000000..72937e5c --- /dev/null +++ b/src/builtins/semver.rs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::ast::{Expr, Ref}; +use crate::builtins; +use crate::builtins::utils::{ensure_args_count, ensure_string}; +use crate::lexer::Span; +use crate::value::Value; + +use semver::Version; + +use std::cmp::Ordering; +use std::collections::HashMap; + +use anyhow::{Ok, Result}; + +pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) { + m.insert("semver.compare", (compare, 2)); + m.insert("semver.is_valid", (is_valid, 1)); +} + +fn compare(span: &Span, params: &[Ref], args: &[Value]) -> Result { + let name = "semver.compare"; + ensure_args_count(span, name, params, args, 2)?; + + let v1 = ensure_string(name, ¶ms[0], &args[0])?; + let v2 = ensure_string(name, ¶ms[1], &args[1])?; + let version1 = Version::parse(&v1)?; + let version2 = Version::parse(&v2)?; + let result = match version1.cmp_precedence(&version2) { + Ordering::Less => -1, + Ordering::Equal => 0, + Ordering::Greater => 1, + }; + Ok(Value::from_float(result as f64)) +} + +fn is_valid(span: &Span, params: &[Ref], args: &[Value]) -> Result { + let name = "semver.is_valid"; + ensure_args_count(span, name, params, args, 1)?; + let v = ensure_string(name, ¶ms[0], &args[0])?; + Ok(Value::Bool(Version::parse(&v).is_ok())) +} diff --git a/tests/interpreter/cases/builtins/semver/compare.yaml b/tests/interpreter/cases/builtins/semver/compare.yaml new file mode 100644 index 00000000..92316f81 --- /dev/null +++ b/tests/interpreter/cases/builtins/semver/compare.yaml @@ -0,0 +1,47 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +cases: + - note: semver.compare passing + data: {} + modules: + - | + package test + + vers = [ + "1.9.10", + "1.8.15" + ] + + # Compare each version against itself and others. + r = [ a | a = semver.compare(vers[_], vers[_]) ] + query: data.test.r + want_result: [0, 1, -1, 1] + skip: true # TODO: remove this + + - note: semver.compare wrong arg1 type + data: {} + modules: + - | + package test + a = semver.compare("", 1) + query: data.test.a + error: expects string argument + + - note: semver.compare wrong arg2 type + data: {} + modules: + - | + package test + a = semver.compare(1, "") + query: data.test.a + error: expects string argument + + - note: semver extra arg + data: {} + modules: + - | + package test + a = semver.compare("", "", "") + query: data.test.a + error: expects 2 arguments diff --git a/tests/interpreter/cases/builtins/semver/is_valid.yaml b/tests/interpreter/cases/builtins/semver/is_valid.yaml new file mode 100644 index 00000000..8ba0aec2 --- /dev/null +++ b/tests/interpreter/cases/builtins/semver/is_valid.yaml @@ -0,0 +1,39 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +cases: + - note: semver.is_valid passing + data: {} + modules: + - | + package test + + vers = [ + "1.9.10", + "1.8.15", + "1.1", + "1.1.12-rc1+foo" + ] + + r = [ a | a = semver.is_valid(vers[_]) ] + query: data.test.r + want_result: [true, true, false] + skip: true # TODO: remove this + + - note: semver.compare wrong arg1 type + data: {} + modules: + - | + package test + a = semver.is_valid(1) + query: data.test.a + error: expects string argument + + - note: semver.is_valid extra arg + data: {} + modules: + - | + package test + a = semver.is_valid("", "") + query: data.test.a + error: expects 1 argument