Skip to content

Commit

Permalink
Add full builder (#11)
Browse files Browse the repository at this point in the history
* Add full builder

First bit for all resource output, imports, and fix mappings in
the output.
  • Loading branch information
iph authored Oct 2, 2021
1 parent 7255d83 commit 484e940
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 47 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ edition = "2018"
clap = "3.0.0-beta.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
nom = "7.0.0"
nom = "7.0.0"
voca_rs = "1.14.0"
19 changes: 5 additions & 14 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use clap::{App, Arg};
use noctilucent::semantic::importer::Importer;
use noctilucent::semantic::reference::ReferenceTable;
use noctilucent::semantic::to_string;
use noctilucent::CloudformationParseTree;
use serde_json::Value;
use std::fs;
use voca_rs::case::camel_case;

fn main() {
let matches = App::new("Transmutes cfn templates to cdk")
Expand All @@ -25,22 +27,11 @@ fn main() {
let cfn_tree = CloudformationParseTree::build(&value).unwrap();
let reference_table = ReferenceTable::new(&cfn_tree);

println!("Amount of parameters: {}", cfn_tree.parameters.params.len());
println!("Amount of mappings: {}", cfn_tree.mappings.mappings.len());
let import = Importer::new(&cfn_tree);

println!(
"Amount of conditions: {}",
cfn_tree.conditions.conditions.len()
);
println!(
"Amount of resources: {}",
cfn_tree.resources.resources.len()
);

println!("====================================");
println!("{}", import.synthesize().join("\n"));
println!("{}", cfn_tree.mappings.synthesize());

println!("====================================");
for (_, cond) in cfn_tree.conditions.conditions.iter() {
println!("{}", cond.synthesize());
}
Expand All @@ -58,7 +49,7 @@ fn main() {
match to_string(prop, &reference_table) {
None => {}
Some(x) => {
println!("\t{}:{},", name, x);
println!("\t{}:{},", camel_case(name), x);
}
}
}
Expand Down
10 changes: 2 additions & 8 deletions src/parser/condition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,7 @@ impl ConditionParseTree {
fn synthesize_condition_recursive(val: &ConditionValue) -> String {
match val {
ConditionValue::And(x) => {
let a: Vec<String> = x
.iter()
.map(|x| synthesize_condition_recursive(x))
.collect();
let a: Vec<String> = x.iter().map(synthesize_condition_recursive).collect();

let inner = a.join(" && ");
format!("({})", inner)
Expand All @@ -68,10 +65,7 @@ fn synthesize_condition_recursive(val: &ConditionValue) -> String {
}
}
ConditionValue::Or(x) => {
let a: Vec<String> = x
.iter()
.map(|x| synthesize_condition_recursive(x))
.collect();
let a: Vec<String> = x.iter().map(synthesize_condition_recursive).collect();

let inner = a.join(" || ");
format!("({})", inner)
Expand Down
43 changes: 24 additions & 19 deletions src/parser/lookup_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,14 @@ impl MappingsParseTree {
pub fn synthesize(&self) -> String {
let mut mappings_ts_str = String::new();
for (mapping_name, mapping) in self.mappings.iter() {
let record_type = match mapping.find_first_type() {
MappingInnerValue::String(_) => "Record<string, Record<string, string>>",
MappingInnerValue::List(_) => "Record<string, Record<string, Array<string>>",
};
mappings_ts_str.push_str(&format!(
"const {} = {}",
"const {}: {} = {}",
mapping_name,
record_type,
mapping.synthesize()
));
}
Expand Down Expand Up @@ -59,40 +64,40 @@ impl MappingParseTree {
}

fn synthesize(&self) -> String {
let mut mapping_parse_tree_ts = String::from("new Map(\n");
let mut mapping_parse_tree_ts = String::from("{\n");
let mut outer_records = Vec::new();
for (outer_mapping_key, inner_mapping) in self.mappings.iter() {
mapping_parse_tree_ts.push_str(&format!(
"\t[{}\t],\n",
synthesize_outer_mapping(outer_mapping_key, inner_mapping)
outer_records.push(format!(
"\t\"{}\": {}",
outer_mapping_key,
synthesize_inner_mapping(inner_mapping)
));
}
mapping_parse_tree_ts.push_str(")\n");

let outer = outer_records.join(",\n");
mapping_parse_tree_ts.push_str(&outer);
mapping_parse_tree_ts.push_str("\n};\n");
mapping_parse_tree_ts
}
}

fn synthesize_outer_mapping(
outer_mapping_entry: &str,
inner_mapping: &HashMap<String, MappingInnerValue>,
) -> String {
format!(
"\"{}\", {}",
outer_mapping_entry,
synthesize_inner_mapping(inner_mapping)
)
fn find_first_type(&self) -> &MappingInnerValue {
let value = self.mappings.values().next().unwrap();
let inner_value = value.values().next().unwrap();
inner_value
}
}

fn synthesize_inner_mapping(inner_mapping: &HashMap<String, MappingInnerValue>) -> String {
let mut inner_mapping_ts_str = String::from("new Map(\n");
let mut inner_mapping_ts_str = String::from("{\n");
let mut inner_mapping_entries = Vec::new();
for (inner_mapping_key, inner_mapping_value) in inner_mapping {
inner_mapping_entries.push(format!(
"\t\t[\"{}\", {}]",
"\t\t\"{}\": {}",
inner_mapping_key, inner_mapping_value
));
}
inner_mapping_ts_str.push_str(&inner_mapping_entries.join(",\n"));
inner_mapping_ts_str.push_str(")\n");
inner_mapping_ts_str.push_str("\n\t}");
inner_mapping_ts_str
}

Expand Down
50 changes: 50 additions & 0 deletions src/semantic/importer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use crate::CloudformationParseTree;
use std::collections::HashSet;

pub struct Importer {
type_names: HashSet<TypeName>,
}

impl Importer {
pub fn new(parse_tree: &CloudformationParseTree) -> Importer {
let mut type_names = HashSet::new();
for resource in parse_tree.resources.resources.iter() {
let type_name = TypeName::new(&resource.resource_type);
type_names.insert(type_name);
}

Importer { type_names }
}

pub fn synthesize(&self) -> Vec<String> {
self.type_names.iter().map(|x| x.synthesize()).collect()
}
}

#[derive(PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
struct TypeName {
organization: String,
service: String,
}

impl TypeName {
// In CloudFormation, typenames are always of the form `<Organization>::<Service>::<Resource>
fn new(name: &str) -> TypeName {
let mut split_ref = name.split("::");

// These must always exist.
let organization = split_ref.next().unwrap().to_ascii_lowercase();
let service = split_ref.next().unwrap().to_ascii_lowercase();

TypeName {
organization,
service,
}
}
fn synthesize(&self) -> String {
return format!(
"import * as {} from '@aws-cdk/{}-{}';",
self.service, self.organization, self.service
);
}
}
43 changes: 38 additions & 5 deletions src/semantic/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::parser::resource::ResourceValue;
use crate::parser::sub::{sub_parse_tree, SubValue};
use crate::semantic::reference::ReferenceTable;
use std::collections::HashMap;

pub mod importer;
pub mod reference;

pub fn to_string(resource_value: &ResourceValue, ref_table: &ReferenceTable) -> Option<String> {
Expand Down Expand Up @@ -45,6 +47,26 @@ pub fn to_string(resource_value: &ResourceValue, ref_table: &ReferenceTable) ->
// just resolve the objects.
let val = to_string(&arr[0], ref_table).unwrap();

let mut excess_map = HashMap::new();
if arr.len() > 1 {
let mut iter = arr.iter();
iter.next();

for obj in iter {
match obj {
ResourceValue::Object(obj) => {
for (key, val) in obj.iter() {
let val_str = to_string(val, ref_table).unwrap();
excess_map.insert(key.to_string(), val_str);
}
}
_ => {
// these aren't possible, so panic
panic!("Isn't possible condition")
}
}
}
}
let vars = sub_parse_tree(val.as_str()).unwrap();
let r: Vec<String> = vars
.iter()
Expand All @@ -54,17 +76,28 @@ pub fn to_string(resource_value: &ResourceValue, ref_table: &ReferenceTable) ->
"AWS::Region" => String::from("${this.region}"),
"AWS::Partition" => String::from("${this.partition}"),
"AWS::AccountId" => String::from("${this.account}"),
x => format!("${{props.{}}}", x),
x => match excess_map.get(x) {
None => {
format!("${{props.{}}}", x)
}
Some(x) => {
format!("${{{}}}", x)
}
},
},
})
.collect();

Option::Some(format!("`{}`", r.join("")))
}
ResourceValue::FindInMap(mapper, first, _second) => {
let mapper_str = to_string(mapper, ref_table).unwrap();
ResourceValue::FindInMap(mapper, first, second) => {
let a: &ResourceValue = mapper.as_ref();
let mapper_str = match a {
ResourceValue::String(x) => x.to_string(),
&_ => to_string(mapper, ref_table).unwrap(),
};
let first_str = to_string(first, ref_table).unwrap();
let second_str = to_string(first, ref_table).unwrap();
let second_str = to_string(second, ref_table).unwrap();

Option::Some(format!("{}[{}][{}]", mapper_str, first_str, second_str))
}
Expand All @@ -78,7 +111,7 @@ pub fn to_string(resource_value: &ResourceValue, ref_table: &ReferenceTable) ->
.unwrap();
let true_expr = to_string(true_expr, ref_table).unwrap();
let false_expr = match to_string(false_expr, ref_table) {
None => String::from("NOPE_WTF"),
None => String::from("{}"),
Some(x) => x,
};

Expand Down

0 comments on commit 484e940

Please sign in to comment.