1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
use crate::command::{Command, Commands}; use crate::element::Element; use crate::state::State; #[allow(unused_imports)] use crate::transformation::{Transformation, TransformationType}; use anyhow::Result; use serde_derive::Deserialize; use std::fs; #[derive(Deserialize, PartialEq, Debug)] pub struct Migration { name: String, description: String, transformations: Vec<Transformation>, } impl Migration { pub fn from_file(file_name: &str) -> Result<Self> { let content = fs::read_to_string(file_name)?; let result = serde_json::from_str(&content)?; Ok(result) } pub fn apply(&self, state: &State) -> (State, Commands) { let mut new_elements = Vec::new(); let mut commands = Vec::new(); for element in state.elements.iter() { let (new_element, mut new_commands) = self.apply_element(element); new_elements.push(new_element); commands.append(&mut new_commands); } ( State { elements: new_elements, }, Commands::new(commands), ) } fn apply_element(&self, element: &Element) -> (Element, Vec<Command>) { let mut acc = element.clone(); let mut commands = Vec::new(); for transformation in self.transformations.iter() { let transformation_result = transformation.apply(&acc); acc = transformation_result.element; commands.push(transformation_result.command); } (acc, commands) } } #[cfg(test)] mod migration_tests { use super::*; fn current() -> State { State { elements: vec![ Element::Resource("docker_container.container".to_string()), Element::Resource("docker_image.image".to_string()), Element::Resource("public_network.docker_network.network".to_string()), ], } } #[test] fn test_from_file_loads_a_migration() { let m = Migration { name: "move to module".to_string(), description: "Convert network to module".to_string(), transformations: vec![Transformation { kind: TransformationType::MV, matcher: "public_network".to_string(), replacement: "module.public_network".to_string(), }], }; assert_eq!( Migration::from_file("fixtures/migrations/move_to_module.json").unwrap(), m ) } #[test] fn test_apply_migration() { let m = Migration::from_file("fixtures/migrations/multi_step.json").unwrap(); let target = State { elements: vec![ Element::Resource("docker_container.container".to_string()), Element::Resource("docker_image.image".to_string()), Element::Resource("module.public_network.docker_network.network".to_string()), ], }; let expected_commands = Commands { elements: vec![Command::MV { source: "public_network.docker_network.network".to_string(), target: "module.public_network.docker_network.network".to_string(), }], }; let (state, commands) = m.apply(¤t()); assert_eq!(target, state); assert_eq!(expected_commands, commands); } }