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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
pub mod context;
pub mod names;
pub mod parsers;
pub mod readers;
pub mod types;
pub mod unparsers;

use crate::*;
use crate::gen::schema::*;
use crate::syntax::block::Formatter;
use crate::syntax::block::constructors::*;

use glob::glob;
use preserves::value::BinarySource;
use preserves::value::IOBinarySource;
use preserves::value::Map;
use preserves::value::Reader;
use preserves::value::Set;

use std::convert::TryFrom;
use std::fs::DirBuilder;
use std::fs::File;
use std::io;
use std::io::BufRead;
use std::io::Read;
use std::io::Write;
use std::path::PathBuf;

pub type ModulePath = Vec<String>;

pub trait Plugin: std::fmt::Debug {
    fn generate(
        &self,
        module_ctxt: &mut context::ModuleContext,
        definition_name: &str,
        definition: &Definition,
    );
}

#[derive(Debug)]
pub struct CompilerConfig {
    pub bundle: Map<ModulePath, Schema>,
    pub output_dir: PathBuf,
    pub fully_qualified_module_prefix: String,
    pub support_crate: String,
    pub module_aliases: Map<ModulePath, String>,
    pub plugins: Vec<Box<dyn Plugin>>,
}

impl CompilerConfig {
    pub fn new(
        output_dir: PathBuf,
        fully_qualified_module_prefix: String,
    ) -> Self {
        CompilerConfig {
            bundle: Map::new(),
            output_dir: output_dir,
            fully_qualified_module_prefix: fully_qualified_module_prefix,
            support_crate: "preserves_schema".to_owned(),
            module_aliases: Map::new(),
            plugins: vec![
                Box::new(types::TypePlugin),
                Box::new(readers::ReaderPlugin),
                Box::new(parsers::ParserPlugin),
                Box::new(unparsers::UnparserPlugin),
            ],
        }
    }

    pub fn load_schemas_and_bundles(&mut self, inputs: &Vec<PathBuf>) -> io::Result<()> {
        for i in inputs {
            let mut f = File::open(&i)?;
            let mut src = IOBinarySource::new(&mut f);
            let mut reader = src.packed_iovalues();
            let blob = reader.demand_next(false)?;

            if let Ok(s) = Schema::try_from(&blob) {
                let prefix = i.file_stem().ok_or_else(
                    || io::Error::new(io::ErrorKind::InvalidData,
                                      format!("Bad schema file stem: {:?}", i)))?
                    .to_str().ok_or_else(
                        || io::Error::new(io::ErrorKind::InvalidData,
                                          format!("Invalid UTF-8 in schema file name: {:?}", i)))?;
                self.bundle.insert(vec![prefix.to_owned()], s);
                continue;
            }

            if let Ok(Bundle { modules }) = Bundle::try_from(&blob) {
                for (ModulePath(k), v) in modules.0 {
                    self.bundle.insert(k, v);
                }
                continue;
            }

            return Err(io::Error::new(io::ErrorKind::InvalidData,
                                      format!("Invalid schema binary blob {:?}", i)));
        }
        Ok(())
    }
}

pub fn expand_inputs(globs: &Vec<String>) -> io::Result<Vec<PathBuf>> {
    let mut result = Vec::new();
    for g in globs.iter() {
        for p in glob(g).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, format!("{}", e)))? {
            result.push(p.map_err(glob::GlobError::into_error)?)
        }
    }
    Ok(result)
}

fn write_if_changed(output_path: &PathBuf, contents: &[u8]) -> io::Result<()> {
    if output_path.exists() {
        if let Ok(mut f) = File::open(output_path) {
            let mut existing_contents = Vec::new();
            f.read_to_end(&mut existing_contents)?;
            if existing_contents == contents {
                return Ok(());
            }
        }
    }
    let mut f = File::create(output_path)?;
    f.write_all(contents)
}

pub fn compile(config: &CompilerConfig) -> io::Result<()> {
    for (k, v) in config.bundle.iter() {
        let mut output_path = config.output_dir.clone();
        output_path.extend(k);
        let module_name = output_path.file_stem().unwrap().to_str().unwrap().to_owned();
        let module_name = names::render_modname(&module_name);
        output_path.set_file_name(format!("{}.rs", module_name));
        DirBuilder::new().recursive(true).create(output_path.parent().unwrap())?;
        let mut m = context::ModuleContext::new(config, &ModulePath(k.clone()));

        let mut modes: Vec<context::ModuleContextMode> =
            vec![context::ModuleContextMode::TargetAny];

        match &v.embedded_type {
            EmbeddedTypeName::False => {
                m.define_type(item(seq!["pub type _Ptr = preserves::value::IOValue;"]));
                m.define_type(item(seq!["pub type _Any = preserves::value::IOValue;"]));
            }
            EmbeddedTypeName::Ref(r) => {
                modes.push(context::ModuleContextMode::TargetIOValue);
                m.define_type(item(seq!["pub type _Dom = ", m.render_ref(&**r), ";"]));
                m.define_type(item(seq!["pub type _Ptr = std::sync::Arc<_Dom>;"]));
                m.define_type(item(seq!["pub type _Any = preserves::value::ArcValue<_Ptr>;"]));
            }
        }

        let modes = modes; // rebind as non-mutable

        for (n, d) in &v.definitions.0 {
            for plugin in config.plugins.iter() {
                plugin.generate(&mut m, n, d);
            }
            for mode in &modes {
                m.mode = Some(*mode);
                for plugin in config.plugins.iter() {
                    plugin.generate(&mut m, n, d);
                }
            }
            m.mode = None;
        }

        //---------------------------------------------------------------------------

        let mut lines: Vec<String> = Vec::new();

        lines.push("#![allow(unused_parens)]".to_owned());
        lines.push("#![allow(unused_imports)]".to_owned());
        lines.push("".to_owned());
        lines.push("use std::convert::TryFrom;".to_owned());
        lines.push("use preserves::value::Domain;".to_owned());
        lines.push("use preserves::value::NestedValue;".to_owned());
        lines.push(format!("use {}::support as _support;", &config.support_crate));
        lines.push("use _support::Deserialize;".to_owned());
        lines.push("".to_owned());

        for mode in &modes {
            m.mode = Some(*mode);
            lines.push(format!("mod {} {{", m.target_prefix()));
            lines.push("    use super::_Any;".to_owned());
            lines.push(format!("    use {}::support as _support;", &config.support_crate));
            lines.push("    _support::lazy_static! {".to_owned());
            for (value, name) in &m.literals {
                let bs = preserves::value::PackedWriter::encode_iovalue(&value).unwrap();
                lines.push(format!(
                    "        pub static ref {}: {} = /* {:?} */ _support::decode_lit(&vec!{:?}).unwrap();",
                    name,
                    m.target(),
                    value,
                    bs));
            }
            lines.push("    }".to_owned());
            lines.push("}".to_owned());
            lines.push("".to_owned());
        }
        m.mode = None;

        for i in m.typedefs {
            lines.push(Formatter::to_string(i));
            lines.push("".to_owned());
        }
        for i in m.functiondefs {
            lines.push(Formatter::to_string(i));
            lines.push("".to_owned());
        }

        {
            let contents = lines.join("\n");
            write_if_changed(&output_path, contents.as_bytes())?;
        }

        {
            let mut mod_rs = output_path.clone();
            mod_rs.set_file_name("mod.rs");
            let mut lines = if !mod_rs.exists() {
                Set::new()
            } else {
                io::BufReader::new(File::open(&mod_rs)?)
                    .lines().collect::<Result<Set<String>, _>>()?
            };
            lines.insert(format!("pub mod {};", &module_name));
            let contents = lines.into_iter().collect::<Vec<String>>().join("\n");
            write_if_changed(&mod_rs, contents.as_bytes())?;
        }
    }
    Ok(())
}