dml_tools/
tools.rs

1use crate::sql::{DBObject, TypeWriter};
2use crate::type_writers::Postgresql;
3use crate::util::*;
4use std::error::Error;
5use std::fs::File;
6use std::io::prelude::*;
7
8type BxTypeWriter = Box<dyn TypeWriter>;
9type BxObject<'a> = &'a Box<&'a dyn DBObject>;
10type Objects = Vec<Box<dyn DBObject>>;
11
12/// DML processor and SQL generator
13///
14/// Collects DBObject's and creates SQL sql_statements using the supplied
15///  TypeWriter or Postgresql if none is provided
16pub struct Processor<'a> {
17    objs: Vec<Box<&'a dyn DBObject>>,
18    type_writer:BxTypeWriter,
19}
20
21impl <'a> Processor<'a> {
22    /// Create a new Processor optionally specifying a TypeWriter to use
23    pub fn new(type_writer:Option<BxTypeWriter>) -> Self {
24        let type_writer = if let Some(tr) = type_writer {
25            tr
26        } else {
27            Box::new(Postgresql{})
28        };
29        Processor {
30            objs: Vec::new(),
31            type_writer,
32        }
33    }
34    /// Create a new Processor optionally specifying a TypeWriter to use
35    pub fn new_with_objects(objects:&'a Objects, type_writer:Option<BxTypeWriter>) -> Self {
36        let mut me = Processor::new(type_writer);
37        for obj in objects.iter() {
38            me.objs.push(Box::new(obj.as_ref()))
39        }
40        me
41    }
42    /// Add a DB object
43    pub fn add(&mut self, object:&'a dyn DBObject) -> &Self {
44        self.objs.push(Box::new(object));
45        self
46    }
47    fn add_to_toplevel(&self, obj:&mut BxObject, out:&mut Vec<String>, delayed:&mut Vec<&Box<&dyn DBObject>>) {
48        let sql;
49        if delayed.is_empty() {
50            // println!("{:?}: SIMPLE to_sql()", obj);
51            sql = obj.to_sql(self.type_writer.as_ref())
52        } else {
53            // println!("{:?}: TOP-LEVEL to_sql()", obj);
54            sql = obj.top_level_to_sql(self.type_writer.as_ref(), &delayed);
55            delayed.clear()
56        };
57        if ! sql.is_empty() {
58            out.push(sql);
59        }
60    }
61    /// Get the list of serialized SQL sql_statements
62    pub fn sql_statements(&self) -> Vec<String> {
63        let mut out = Vec::new();
64        let mut delayed = Vec::new();
65        let mut top : Option<BxObject> = None;
66        for obj in &self.objs {
67            if ! obj.is_top_level() {
68                delayed.push(obj)
69            } else {
70                if let Some(mut tl) = top {
71                    self.add_to_toplevel(&mut tl, &mut out, &mut delayed);
72                }
73                top = Some(obj)
74            }
75        }
76        if let Some(mut tl) = top {
77            self.add_to_toplevel(&mut tl, &mut out, &mut delayed)
78        }
79        out
80    }
81    /// Get a String with all of the SQL statments
82    pub fn join_sql_statements(&self) -> String {
83        self.sql_statements().join("\n")
84    }
85    /// Write objects to a YAML file
86    pub fn serialize_to_yaml_file(&self, file_name:&str) -> Result<(), Box<dyn Error>> {
87        Ok(write_yaml_to_file(file_name, &self.objs)?)
88    }
89    /// Write generated SQL to file
90    pub fn write_to_sql_file(&self, file_name:&str) -> Result<(), Box<dyn Error>> {
91        let sqls = self.join_sql_statements();
92        let mut fh = File::create(file_name)?;
93        Ok(fh.write_all(&sqls.as_bytes())?)
94    }
95    /// Get number of objects present
96    pub fn objects(&self) -> &Vec<Box<&'a dyn DBObject>> {
97        &self.objs
98    }
99}
100
101
102/// DML yaml loader
103///
104/// Loads DBObject's from a .yaml file and permits accesing them
105/// Used for giving scope to Processor
106pub struct Loader {
107    objs: Objects,
108}
109
110impl Loader {
111    /// Create Loader from YAML in a String
112    pub fn new(data:&str) -> Result<Self, Box<dyn Error>> {
113        Ok(Loader {
114            objs: read_yaml_from_string(data).expect(format!("To load objects from string '{data}'").as_str())
115        })
116    }
117    /// Create Loader reading from a YAML file
118    pub fn new_from_file(file_name:&str) -> Result<Self, Box<dyn Error>> {
119        Ok(Loader {
120            objs: read_yaml_from_file(file_name).expect(format!("To load objects from '{file_name}'").as_str())
121        })
122    }
123    pub fn objects(&self) -> &Objects {
124        &self.objs
125    }
126}