Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Output processing #38

Merged
merged 2 commits into from
Jan 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/ir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ use crate::ir::conditions::ConditionInstruction;
use crate::ir::constructor::Constructor;
use crate::ir::importer::ImportInstruction;
use crate::ir::mappings::MappingInstruction;
use crate::ir::outputs::OutputInstruction;
use crate::ir::resources::ResourceInstruction;
use crate::{CloudformationParseTree, TransmuteError};

pub mod conditions;
pub mod constructor;
pub mod importer;
pub mod mappings;
pub mod outputs;
pub mod reference;
pub mod resources;

Expand All @@ -18,6 +20,7 @@ pub struct CloudformationProgramIr {
pub conditions: Vec<ConditionInstruction>,
pub mappings: Vec<MappingInstruction>,
pub resources: Vec<ResourceInstruction>,
pub outputs: Vec<OutputInstruction>,
}

impl CloudformationProgramIr {
Expand All @@ -28,6 +31,7 @@ impl CloudformationProgramIr {
conditions: Vec::new(),
mappings: Vec::new(),
resources: Vec::new(),
outputs: Vec::new(),
}
}

Expand All @@ -42,12 +46,14 @@ impl CloudformationProgramIr {
let constructor = constructor::Constructor::translate(parse_tree);
let mappings = mappings::translate(parse_tree);
let resources = resources::translates_resources(parse_tree);
let outputs = outputs::translate(parse_tree);
Ok(CloudformationProgramIr {
imports,
constructor,
conditions,
mappings,
resources,
outputs,
})
}
}
34 changes: 34 additions & 0 deletions src/ir/outputs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use crate::ir::resources::{translate_resource, ResourceIr, ResourceTranslationInputs};
use crate::specification::{Complexity, SimpleType};
use crate::CloudformationParseTree;

pub struct OutputInstruction {
pub name: String,
pub export: Option<ResourceIr>,
pub value: ResourceIr,
}

pub fn translate(parse_tree: &CloudformationParseTree) -> Vec<OutputInstruction> {
let outputs = &parse_tree.outputs;
let mut instructions = Vec::new();
for (name, output) in outputs.outputs.iter() {
let resource_translator = ResourceTranslationInputs {
parse_tree,
complexity: Complexity::Simple(SimpleType::Json),
resource_metadata: None,
};

let value = translate_resource(&output.value, &resource_translator).unwrap();
let mut export = Option::None;
if let Some(x) = &output.export {
export = Option::Some(translate_resource(x, &resource_translator).unwrap());
}

instructions.push(OutputInstruction {
name: name.to_string(),
export,
value,
})
}
instructions
}
65 changes: 32 additions & 33 deletions src/ir/resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,14 @@ pub enum ResourceIr {
/// for resource types.
#[derive(Clone, Debug)]
pub struct ResourceTranslationInputs<'t> {
parse_tree: &'t CloudformationParseTree,
pub parse_tree: &'t CloudformationParseTree,
pub complexity: Complexity,
pub resource_metadata: Option<ResourceMetadata<'t>>,
}

#[derive(Clone, Debug)]
pub struct ResourceMetadata<'t> {
specification: &'t Specification,
complexity: Complexity,
property_type: Option<&'t str>,
resource_type: &'t str,
}
Expand Down Expand Up @@ -75,33 +80,19 @@ pub fn translates_resources(parse_tree: &CloudformationParseTree) -> Vec<Resourc
let property_type = property_type.as_deref();
let rt = ResourceTranslationInputs {
parse_tree,
specification: &spec,
complexity: property_rule.get_complexity(),
property_type,
resource_type: &resource.resource_type,
resource_metadata: Option::Some(ResourceMetadata {
specification: &spec,
property_type,
resource_type: &resource.resource_type,
}),
};

let ir = translate_resource(prop, &rt).unwrap();
props.insert(name.to_string(), ir);
}
let metadata = optional_ir_json(&spec, parse_tree, &resource.metadata).unwrap();

let mut update_policy: Option<ResourceIr> = Option::None;
if let Some(x) = &resource.update_policy {
let complexity = Complexity::Simple(SimpleType::Json);
let property_type = Option::None;
let rt = ResourceTranslationInputs {
parse_tree,
complexity,
specification: &spec,
property_type,
resource_type: "",
};

let ir = translate_resource(x, &rt).unwrap();
update_policy = Option::Some(ir);
}

let metadata = optional_ir_json(parse_tree, &resource.metadata).unwrap();
let update_policy = optional_ir_json(parse_tree, &resource.update_policy).unwrap();
resource_instructions.push(ResourceInstruction {
name: resource.name.to_string(),
resource_type: resource.resource_type.to_string(),
Expand Down Expand Up @@ -143,20 +134,16 @@ fn order(resource_instructions: Vec<ResourceInstruction>) -> Vec<ResourceInstruc
}

fn optional_ir_json(
spec: &Specification,
parse_tree: &CloudformationParseTree,
input: &Option<ResourceValue>,
) -> Result<Option<ResourceIr>, TransmuteError> {
let mut policy: Option<ResourceIr> = Option::None;
if let Some(x) = input {
let complexity = Complexity::Simple(SimpleType::Json);
let property_type = Option::None;
let rt = ResourceTranslationInputs {
parse_tree,
complexity,
specification: spec,
property_type,
resource_type: "",
resource_metadata: Option::None,
};

let ir = translate_resource(x, &rt).unwrap();
Expand Down Expand Up @@ -224,7 +211,7 @@ fn find_dependencies(
}
}

fn translate_resource(
pub fn translate_resource(
resource_value: &ResourceValue,
resource_translator: &ResourceTranslationInputs,
) -> Result<ResourceIr, TransmuteError> {
Expand Down Expand Up @@ -253,19 +240,31 @@ fn translate_resource(
Complexity::Complex(_) => {
// Update the rule with it's underlying property rule.
let mut new_rt = resource_translator.clone();
let rule = resource_translator
let resource_metadata =
resource_translator.resource_metadata.as_ref().unwrap();
let rule = resource_metadata
.specification
.property_types
.get(&resource_translator.property_type.unwrap().to_string())
.get(
&resource_translator
.resource_metadata
.as_ref()
.unwrap()
.property_type
.unwrap()
.to_string(),
)
.unwrap();
let properties = rule.properties.as_ref().unwrap();
let property_rule = properties.get(s).unwrap();
new_rt.complexity = property_rule.get_complexity();
let opt = Specification::full_property_name(
&property_rule.get_complexity(),
resource_translator.resource_type,
resource_metadata.resource_type,
);
new_rt.property_type = opt.as_deref();
let mut new_metadata = resource_metadata.clone();
new_metadata.property_type = opt.as_deref();
new_rt.resource_metadata.replace(new_metadata);
translate_resource(rv, &new_rt)?
}
};
Expand Down
22 changes: 19 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use crate::parser::condition::{build_conditions, ConditionsParseTree};
use crate::parser::lookup_table::{build_mappings, MappingsParseTree};
use crate::parser::output::{build_outputs, OutputsParseTree};
use crate::parser::parameters::{build_parameters, Parameters};
use crate::parser::resource::{build_resources, ResourceValue, ResourcesParseTree};
use serde_json::Value;
Expand Down Expand Up @@ -46,6 +47,7 @@ pub struct CloudformationParseTree {
pub mappings: MappingsParseTree,
pub conditions: ConditionsParseTree,
pub resources: ResourcesParseTree,
pub outputs: OutputsParseTree,
}

impl CloudformationParseTree {
Expand All @@ -54,16 +56,30 @@ impl CloudformationParseTree {
None => Parameters::new(),
Some(params) => build_parameters(params)?,
};
let conditions = build_conditions(json_obj["Conditions"].as_object().unwrap())?;

let conditions = match json_obj["Conditions"].as_object() {
None => ConditionsParseTree::new(),
Some(x) => build_conditions(x)?,
};

// All stacks must have resources, so no checking.
let resources = build_resources(json_obj["Resources"].as_object().unwrap())?;
let mappings: MappingsParseTree =
build_mappings(json_obj["Mappings"].as_object().unwrap())?;

let mappings = match json_obj["Mappings"].as_object() {
None => MappingsParseTree::new(),
Some(x) => build_mappings(x)?,
};
let outputs = match json_obj["Outputs"].as_object() {
None => OutputsParseTree::new(),
Some(x) => build_outputs(x)?,
};

Ok(CloudformationParseTree {
parameters,
conditions,
resources,
mappings,
outputs,
})
}
}
19 changes: 16 additions & 3 deletions src/parser/condition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,23 @@ pub struct ConditionsParseTree {
pub conditions: HashMap<String, ConditionParseTree>,
}

impl ConditionsParseTree {
pub fn new() -> ConditionsParseTree {
ConditionsParseTree {
conditions: HashMap::new(),
}
}
}

impl Default for ConditionsParseTree {
fn default() -> Self {
Self::new()
}
}

pub fn build_conditions(vals: &Map<String, Value>) -> Result<ConditionsParseTree, TransmuteError> {
let mut conditions = ConditionsParseTree {
conditions: HashMap::new(),
};
let mut conditions = ConditionsParseTree::new();

for (name, obj) in vals {
let cond = build_condition_recursively(name, obj)?;
let condition = ConditionParseTree {
Expand Down
1 change: 1 addition & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod condition;
pub mod lookup_table;
pub mod output;
pub mod parameters;
pub mod resource;
pub mod sub;
74 changes: 74 additions & 0 deletions src/parser/output.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use crate::parser::resource::build_resources_recursively;
use crate::{ResourceValue, TransmuteError};
use serde_json::{Map, Value};
use std::collections::HashMap;

#[derive(Debug)]
pub struct OutputsParseTree {
pub outputs: HashMap<String, Output>,
}

impl OutputsParseTree {
pub fn new() -> OutputsParseTree {
OutputsParseTree {
outputs: HashMap::new(),
}
}

pub fn add(&mut self, output: Output) {
self.outputs.insert(output.logical_name.clone(), output);
}
}

#[derive(Debug)]
pub struct Output {
// This is the top level name, also stored in the hash
pub logical_name: String,
pub value: ResourceValue, // TODO - I think this is limited, may want to make it an enum.
pub export: Option<ResourceValue>,
}

impl Output {
fn new(logical_name: String, value: ResourceValue, export: Option<ResourceValue>) -> Output {
Output {
logical_name,
value,
export,
}
}
}

impl Default for OutputsParseTree {
fn default() -> Self {
Self::new()
}
}

pub fn build_outputs(vals: &Map<String, Value>) -> Result<OutputsParseTree, TransmuteError> {
let mut outputs = OutputsParseTree::new();
for (logical_id, value) in vals.iter() {
let val = match value.get("Value") {
None => {
// All outputs *MUST* have a value. Fail
return Err(TransmuteError::new(
"All outputs must have a value, but this does not",
));
}
Some(x) => build_resources_recursively(logical_id, x)?,
};

// For all Exports that exist, it must have a Name object, if either don't exist, don't record.
let export = match value.get("Export").and_then(|x| x.get("Name")) {
None => Option::None,
Some(x) => Option::Some(build_resources_recursively(logical_id, x)?),
};

outputs.add(Output {
logical_name: logical_id.to_string(),
value: val,
export,
});
}

Ok(outputs)
}
7 changes: 5 additions & 2 deletions src/parser/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,10 @@ pub fn build_resources(
Ok(ResourcesParseTree { resources })
}

fn build_resources_recursively(name: &str, obj: &Value) -> Result<ResourceValue, TransmuteError> {
pub fn build_resources_recursively(
name: &str,
obj: &Value,
) -> Result<ResourceValue, TransmuteError> {
let val = match obj {
Value::String(x) => return Ok(ResourceValue::String(x.to_string())),
Value::Object(x) => x,
Expand Down Expand Up @@ -139,7 +142,7 @@ fn build_resources_recursively(name: &str, obj: &Value) -> Result<ResourceValue,
_ => {
return Err(TransmuteError {
details: format!(
"Fn::Sub can only be eitehr an array or a string {}",
"Fn::Sub can only be either an array or a string {}",
name
),
});
Expand Down
Loading