diff --git a/src/recipe/custom_yaml.rs b/src/recipe/custom_yaml.rs index f85416321..224dd4d9d 100644 --- a/src/recipe/custom_yaml.rs +++ b/src/recipe/custom_yaml.rs @@ -432,6 +432,14 @@ impl ScalarNode { _ => None, } } + + /// Convert the scalar node to an integer and follow coercion rules + pub fn as_integer(&self) -> Option { + if !self.may_coerce { + return None; + } + self.value.parse().ok() + } } impl HasSpan for ScalarNode { diff --git a/src/recipe/custom_yaml/rendered.rs b/src/recipe/custom_yaml/rendered.rs index ad78c4206..173b8c235 100644 --- a/src/recipe/custom_yaml/rendered.rs +++ b/src/recipe/custom_yaml/rendered.rs @@ -234,6 +234,7 @@ pub struct RenderedScalarNode { span: marked_yaml::Span, source: String, value: String, + may_coerce: bool, } impl Serialize for RenderedScalarNode { @@ -246,16 +247,22 @@ impl Serialize for RenderedScalarNode { } impl RenderedScalarNode { - pub fn new(span: marked_yaml::Span, source: String, value: String) -> Self { + pub fn new(span: marked_yaml::Span, source: String, value: String, may_coerce: bool) -> Self { Self { span, source, value, + may_coerce, } } pub fn new_blank() -> Self { - Self::new(marked_yaml::Span::new_blank(), String::new(), String::new()) + Self::new( + marked_yaml::Span::new_blank(), + String::new(), + String::new(), + false, + ) } /// Treat the scalar node as a string @@ -286,12 +293,22 @@ impl RenderedScalarNode { /// /// Everything else is not a boolean and so will return None pub fn as_bool(&self) -> Option { + if !self.may_coerce { + return None; + } match self.value.as_str() { "true" | "True" | "TRUE" => Some(true), "false" | "False" | "FALSE" => Some(false), _ => None, } } + + pub fn as_integer(&self) -> Option { + if !self.may_coerce { + return None; + } + self.value.parse().ok() + } } impl HasSpan for RenderedScalarNode { @@ -340,6 +357,7 @@ impl<'a> From<&'a str> for RenderedScalarNode { marked_yaml::Span::new_blank(), value.to_owned(), value.to_owned(), + false, ) } } @@ -347,7 +365,7 @@ impl<'a> From<&'a str> for RenderedScalarNode { impl From for RenderedScalarNode { /// Convert from any owned string into a node fn from(value: String) -> Self { - Self::new(marked_yaml::Span::new_blank(), value.clone(), value) + Self::new(marked_yaml::Span::new_blank(), value.clone(), value, false) } } @@ -377,6 +395,7 @@ impl From<&MarkedScalarNode> for RenderedScalarNode { *value.span(), value.as_str().to_owned(), value.as_str().to_owned(), + value.may_coerce(), ) } } @@ -639,6 +658,7 @@ impl Render for Node { *n.span(), n.as_str().to_owned(), n.as_str().to_owned(), + false, ))), } } @@ -653,7 +673,13 @@ impl Render for ScalarNode { label = jinja_error_to_label(&err), )] })?; - let rendered = RenderedScalarNode::new(*self.span(), self.as_str().to_string(), rendered); + // unsure whether this should be allowed to coerce // check if it's quoted? + let rendered = RenderedScalarNode::new( + *self.span(), + self.as_str().to_string(), + rendered, + self.may_coerce, + ); if rendered.is_empty() { Ok(RenderedNode::Null(rendered)) @@ -677,7 +703,12 @@ impl Render> for ScalarNode { )] })?; - let rendered = RenderedScalarNode::new(*self.span(), self.as_str().to_string(), rendered); + let rendered = RenderedScalarNode::new( + *self.span(), + self.as_str().to_string(), + rendered, + self.may_coerce, + ); if rendered.is_empty() { Ok(None) @@ -708,6 +739,7 @@ impl Render for MappingNode { *key.span(), key.as_str().to_owned(), key.as_str().to_owned(), + false, ); let value: RenderedNode = value.render(jinja, &format!("{name}.{}", key.as_str()))?; if value.is_null() { @@ -730,6 +762,7 @@ impl Render for SequenceNode { *self.span(), String::new(), String::new(), + false, ))); } diff --git a/src/recipe/parser.rs b/src/recipe/parser.rs index 788ba282f..2ce144474 100644 --- a/src/recipe/parser.rs +++ b/src/recipe/parser.rs @@ -184,7 +184,7 @@ impl Recipe { if let Some(rendered) = rendered { let variable = if let Some(value) = rendered.as_bool() { Variable::from(value) - } else if let Some(value) = rendered.as_i64() { + } else if let Some(value) = rendered.as_integer() { Variable::from(value) } else { Variable::from_string(&rendered) diff --git a/src/recipe/variable.rs b/src/recipe/variable.rs index 4738fd0b8..762a3df76 100644 --- a/src/recipe/variable.rs +++ b/src/recipe/variable.rs @@ -85,7 +85,7 @@ impl TryConvertNode for RenderedScalarNode { fn try_convert(&self, _name: &str) -> Result> { let variable = if let Some(value) = self.as_bool() { Variable::from(value) - } else if let Some(value) = self.as_i64() { + } else if let Some(value) = self.as_integer() { Variable::from(value) } else { Variable::from_string(self) diff --git a/src/snapshots/rattler_build__variant_config__tests__flatten_selectors-2.snap b/src/snapshots/rattler_build__variant_config__tests__flatten_selectors-2.snap index 6286db277..213020615 100644 --- a/src/snapshots/rattler_build__variant_config__tests__flatten_selectors-2.snap +++ b/src/snapshots/rattler_build__variant_config__tests__flatten_selectors-2.snap @@ -7,7 +7,7 @@ zip_keys: ~ c_compiler: - vs2019 cplusplus: - - 100 + - "100" cxx_compiler: - vs2019 python: diff --git a/src/snapshots/rattler_build__variant_config__tests__flatten_selectors.snap b/src/snapshots/rattler_build__variant_config__tests__flatten_selectors.snap index 0f56adb94..ab519b408 100644 --- a/src/snapshots/rattler_build__variant_config__tests__flatten_selectors.snap +++ b/src/snapshots/rattler_build__variant_config__tests__flatten_selectors.snap @@ -7,8 +7,8 @@ zip_keys: ~ c_compiler: - super_g++ cplusplus: - - 10 - - 1000 + - "10" + - "1000" cxx_compiler: - super_gcc python: diff --git a/src/snapshots/rattler_build__variant_config__tests__load_config.snap b/src/snapshots/rattler_build__variant_config__tests__load_config.snap index cf86b2698..793b7d8b2 100644 --- a/src/snapshots/rattler_build__variant_config__tests__load_config.snap +++ b/src/snapshots/rattler_build__variant_config__tests__load_config.snap @@ -66,8 +66,12 @@ blas_impl: - openblas - mkl - blis +boolean: + - true build_platform: - linux-64 +integer: + - 5 libblas: - 3.9 *netlib libcblas: @@ -76,5 +80,9 @@ liblapack: - 3.9 *netlib liblapacke: - 3.9 *netlib +noboolean: + - "true" +nointeger: + - "5" target_platform: - linux-64 diff --git a/src/used_variables.rs b/src/used_variables.rs index d6e079555..278954a0f 100644 --- a/src/used_variables.rs +++ b/src/used_variables.rs @@ -73,6 +73,9 @@ fn extract_variable_from_expression(expr: &Expr, variables: &mut HashSet Expr::Var(var) => { variables.insert(var.id.into()); } + Expr::Test(test) => { + extract_variable_from_expression(&test.expr, variables); + } Expr::BinOp(binop) => { extract_variable_from_expression(&binop.left, variables); extract_variable_from_expression(&binop.right, variables); @@ -337,6 +340,7 @@ mod test { - ${{ pin_compatible(abc ~ def) }} - if: match(xpython, ">=3.7") then: numpy 100 + - ${{ testexprvar is string }} "#; let recipe_node = crate::recipe::custom_yaml::Node::parse_yaml(0, recipe).unwrap(); @@ -354,6 +358,7 @@ mod test { assert!(used_vars.contains("abc")); assert!(used_vars.contains("def")); assert!(used_vars.contains("xpython")); + assert!(used_vars.contains("testexprvar")); } #[test] diff --git a/src/variant_config.rs b/src/variant_config.rs index 9e94396cd..fd269b362 100644 --- a/src/variant_config.rs +++ b/src/variant_config.rs @@ -260,6 +260,7 @@ impl VariantConfig { let rendered_node: RenderedNode = yaml_node .render(&jinja, path.to_string_lossy().as_ref()) .map_err(|e| ParseErrors::from_partial_vec(source.clone(), e))?; + let config: VariantConfig = rendered_node .try_convert(path.to_string_lossy().as_ref()) .map_err(|e| { @@ -577,9 +578,9 @@ impl TryConvertNode for RenderedMappingNode { config.zip_keys = value.try_convert(key_str)?; } _ => { - let variants: Option> = value.try_convert(key_str)?; + let variants: Option> = value.try_convert(key_str)?; if let Some(variants) = variants { - config.variants.insert(key_str.into(), variants.clone()); + config.variants.insert(key_str.into(), variants); } } } @@ -740,7 +741,10 @@ mod tests { }; let variant = VariantConfig::from_files(&[yaml_file], &selector_config).unwrap(); - + assert_eq!( + variant.variants.get(&"noboolean".into()).unwrap(), + &vec![Variable::from_string("true")] + ); insta::assert_yaml_snapshot!(variant); } diff --git a/test-data/recipes/jinja-types/recipe.yaml b/test-data/recipes/jinja-types/recipe.yaml index 9f0a18ce1..c8d7e0f28 100644 --- a/test-data/recipes/jinja-types/recipe.yaml +++ b/test-data/recipes/jinja-types/recipe.yaml @@ -7,6 +7,7 @@ context: cfloat: 1.23 cstring: "blah" cboolean: true + cquoted_bool: "true" cquoted: "5" is_integer: ${{ xinteger is integer }} is_float: ${{ xfloat is float }} @@ -17,6 +18,13 @@ context: is_cstring: ${{ cstring is string }} is_cboolean: ${{ cboolean is boolean }} is_cquoted: ${{ cquoted is string }} + computed_int: ${{ "12.3" | replace(".", "") | int }} + is_computed_int: ${{ computed_int is integer }} + is_quoted_true: ${{ cquoted_bool is string }} + # quoted bool is quoted in variants.yaml and thus should be a string + is_quoted_true_var: ${{ quoted_bool is string }} + is_quoted_int_var: ${{ quoted_int is string }} + package: name: testtypes diff --git a/test-data/recipes/jinja-types/variants.yaml b/test-data/recipes/jinja-types/variants.yaml index 81a5baac9..e8085b3f9 100644 --- a/test-data/recipes/jinja-types/variants.yaml +++ b/test-data/recipes/jinja-types/variants.yaml @@ -6,3 +6,7 @@ xstring: - "blah" xboolean: - true +quoted_bool: + - "true" +quoted_int: + - "5" diff --git a/test-data/variant_files/variant_config_1.yaml b/test-data/variant_files/variant_config_1.yaml index fac6f4201..3ec9215ed 100644 --- a/test-data/variant_files/variant_config_1.yaml +++ b/test-data/variant_files/variant_config_1.yaml @@ -74,3 +74,7 @@ arrow_cpp: assimp: - 5.2.5 aws_sdk_cpp: "4.5" +integer: 5 +boolean: true +noboolean: "true" +nointeger: "5" diff --git a/test/end-to-end/__snapshots__/test_simple/test_jinja_types.1.json b/test/end-to-end/__snapshots__/test_simple/test_jinja_types.1.json index 7312c7ff7..0b8cb4dd8 100644 --- a/test/end-to-end/__snapshots__/test_simple/test_jinja_types.1.json +++ b/test/end-to-end/__snapshots__/test_simple/test_jinja_types.1.json @@ -1,4 +1,6 @@ { + "quoted_bool": "true", + "quoted_int": "5", "xboolean": true, "xfloat": "1.23", "xinteger": 5, diff --git a/test/end-to-end/__snapshots__/test_simple/test_jinja_types.json b/test/end-to-end/__snapshots__/test_simple/test_jinja_types.json index 8a9826c4e..e498c5614 100644 --- a/test/end-to-end/__snapshots__/test_simple/test_jinja_types.json +++ b/test/end-to-end/__snapshots__/test_simple/test_jinja_types.json @@ -3,7 +3,9 @@ "cboolean": true, "cfloat": "1.23", "cinteger": 5, + "computed_int": 123, "cquoted": "5", + "cquoted_bool": "true", "cstring": "blah", "float": "1.23", "integer": 5, @@ -11,10 +13,14 @@ "is_cboolean": true, "is_cfloat": false, "is_cinteger": true, + "is_computed_int": true, "is_cquoted": true, "is_cstring": true, "is_float": false, "is_integer": true, + "is_quoted_int_var": true, + "is_quoted_true": true, + "is_quoted_true_var": true, "is_string": true, "string": "blah" }