diff --git a/src/ir/conditions/mod.rs b/src/ir/conditions/mod.rs index 5dac8f62..1ecbfa45 100644 --- a/src/ir/conditions/mod.rs +++ b/src/ir/conditions/mod.rs @@ -45,6 +45,8 @@ pub enum ConditionIr { // Cloudformation meta-functions Map(String, Box, Box), + Split(String, Box), + Select(String, Box), // End of recursion, the base primitives to work with Str(String), @@ -94,6 +96,14 @@ impl ConditionValue { ConditionIr::Map(name, Box::new(x), Box::new(y)) } + Self::Split(delimiter, x) => { + let x = x.into_ir(); + ConditionIr::Split(delimiter, Box::new(x)) + } + Self::Select(index, x) => { + let x = x.into_ir(); + ConditionIr::Select(index, Box::new(x)) + } Self::String(x) => ConditionIr::Str(x), Self::Ref(name) => { // The only 2 references allowed in conditions is parameters or pseudo parameters. @@ -180,6 +190,12 @@ impl ConditionValue { key1.find_dependencies(logical_id, topo_sort); key2.find_dependencies(logical_id, topo_sort); } + Self::Split(_, key1) => { + key1.find_dependencies(logical_id, topo_sort); + } + Self::Select(_, key1) => { + key1.find_dependencies(logical_id, topo_sort); + } Self::Function(func) => func.find_dependencies(logical_id, topo_sort), Self::Ref(_) | Self::String(_) => {} } diff --git a/src/parser/condition/mod.rs b/src/parser/condition/mod.rs index 20e3f28a..57a033ca 100644 --- a/src/parser/condition/mod.rs +++ b/src/parser/condition/mod.rs @@ -116,7 +116,8 @@ pub enum ConditionValue { // Cloudformation meta-functions FindInMap(String, Box, Box), - + Split(String, Box), + Select(String, Box), // End of recursion, the base primitives to work with String(String), Ref(String), @@ -160,6 +161,14 @@ impl<'de> serde::Deserialize<'de> for ConditionValue { second_level_key, )) } + "Split" => { + let (delimiter, source_string) = data.newtype_variant()?; + Ok(Self::Value::Split(delimiter, source_string)) + } + "Select" => { + let (index, source_array) = data.newtype_variant()?; + Ok(Self::Value::Select(index, source_array)) + } "Ref" => Ok(Self::Value::Ref(data.newtype_variant()?)), other => Ok(ConditionFunction::from_variant_access(other, data)?.into()), } @@ -195,6 +204,14 @@ impl<'de> serde::Deserialize<'de> for ConditionValue { second_level_key, )) } + "!Split" | "Fn::Split" => { + let (delimiter, split_str) = data.next_value()?; + Ok(Self::Value::Split(delimiter, split_str)) + } + "!Select" | "Fn::Select" => { + let (index, array) = data.next_value()?; + Ok(Self::Value::Select(index, array)) + } "!Ref" | "Ref" => Ok(Self::Value::Ref(data.next_value()?)), other => Ok(ConditionFunction::from_map_access(other, &mut data)?.into()), } diff --git a/src/parser/condition/tests.rs b/src/parser/condition/tests.rs index 8cd58d50..05792391 100644 --- a/src/parser/condition/tests.rs +++ b/src/parser/condition/tests.rs @@ -181,6 +181,22 @@ fn condition_find_in_map() { ); } +#[test] +fn condition_split() { + let expected = ConditionValue::Split( + ",".into(), + Box::new(ConditionValue::String("hello,world".into())), + ); + assert_eq!( + expected, + serde_yaml::from_str("!Split [\",\", \"hello,world\"]").unwrap() + ); + assert_eq!( + expected, + serde_yaml::from_str("Fn::Split: [\",\", \"hello,world\"]").unwrap() + ); +} + #[test] fn condition_str_bool() { let expected = ConditionValue::String("true".into()); diff --git a/src/synthesizer/golang/mod.rs b/src/synthesizer/golang/mod.rs index 63f6a2a0..bb7cb4d7 100644 --- a/src/synthesizer/golang/mod.rs +++ b/src/synthesizer/golang/mod.rs @@ -360,6 +360,8 @@ impl Inspectable for ConditionIr { list.iter().any(|cond| cond.uses_map_table(name)) } ConditionIr::Map(map_name, _, _) => map_name == name, + ConditionIr::Split(_, cond) => cond.uses_map_table(name), + ConditionIr::Select(_, cond) => cond.uses_map_table(name), ConditionIr::Str(_) | ConditionIr::Ref(_) => false, } } @@ -494,6 +496,16 @@ impl GolangEmitter for ConditionIr { slk.emit_golang(context, output, None); output.text("]"); } + ConditionIr::Split(sep, str) => { + output.text(format!("cdk.Fn_Split(jsii.String({sep:?}), ")); + str.emit_golang(context, output, None); + output.text(")"); + } + ConditionIr::Select(index, str) => { + output.text(format!("cdk.Fn_Select(jsii.Number({index:?}), ")); + str.emit_golang(context, output, None); + output.text(")"); + } } if let Some(trailer) = trailer { output.text(trailer.to_owned()) diff --git a/src/synthesizer/typescript/mod.rs b/src/synthesizer/typescript/mod.rs index 6fc9327e..712d98a2 100644 --- a/src/synthesizer/typescript/mod.rs +++ b/src/synthesizer/typescript/mod.rs @@ -635,6 +635,18 @@ fn synthesize_condition_recursive(val: &ConditionIr) -> String { synthesize_condition_recursive(l2.as_ref()) ) } + ConditionIr::Split(sep, l1) => { + let str = synthesize_condition_recursive(l1.as_ref()); + format!( + "{str}.split('{sep}')", + str = str.escape_debug(), + sep = sep.escape_debug() + ) + } + ConditionIr::Select(index, l1) => { + let str = synthesize_condition_recursive(l1.as_ref()); + format!("cdk.Fn.select({index}, {str})") + } } } diff --git a/tests/end-to-end/simple/app.go b/tests/end-to-end/simple/app.go index 43dec754..852f233b 100644 --- a/tests/end-to-end/simple/app.go +++ b/tests/end-to-end/simple/app.go @@ -90,6 +90,8 @@ func NewNoctStack(scope constructs.Construct, id string, props NoctStackProps) * stack := cdk.NewStack(scope, &id, &props.StackProps) + isUs := cdk.Fn_Select(jsii.Number("0"), cdk.Fn_Split(jsii.String("-"), stack.Region())) == jsii.String("us") + isUsEast1 := stack.Region() == jsii.String("us-east-1") queue := sqs.NewCfnQueue( diff --git a/tests/end-to-end/simple/app.ts b/tests/end-to-end/simple/app.ts index 6c3bc7f6..c26765a5 100644 --- a/tests/end-to-end/simple/app.ts +++ b/tests/end-to-end/simple/app.ts @@ -77,6 +77,7 @@ export class NoctStack extends cdk.Stack { }; // Conditions + const isUs = cdk.Fn.select(0, this.region.split('-')) === 'us'; const isUsEast1 = this.region === 'us-east-1'; // Resources diff --git a/tests/end-to-end/simple/template.yml b/tests/end-to-end/simple/template.yml index c4a7dccc..704ada75 100644 --- a/tests/end-to-end/simple/template.yml +++ b/tests/end-to-end/simple/template.yml @@ -37,7 +37,14 @@ Conditions: Fn::Equals: - !Ref AWS::Region - us-east-1 - + IsUs: + Fn::Equals: + - Fn::Select: + - 0 + - Fn::Split: + - "-" + - "!Ref": AWS::Region + - us Parameters: BucketNamePrefix: Type: String