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; #[derive(Debug)] pub struct CompilerConfig { pub bundle: Map, pub output_dir: PathBuf, pub fully_qualified_module_prefix: String, pub support_crate: String, pub module_aliases: Map, } 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(), } } pub fn load_schemas_and_bundles(&mut self, inputs: &Vec) -> 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) -> io::Result> { 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 = 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 { m.define_type(item(types::render_definition_type(&m, n, &types::definition_type(d)))); m.define_type(item(seq!["impl preserves::value::Domain for ", names::render_constructor(n), " {}"])); for mode in &modes { m.mode = *mode; parsers::gen_definition_parser(&mut m, n, d); unparsers::gen_definition_unparser(&mut m, n, d); } readers::gen_definition_reader(&mut m, n, d); } //--------------------------------------------------------------------------- let mut lines: Vec = 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 = *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()); } 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::, _>>()? }; lines.insert(format!("pub mod {};", &module_name)); let contents = lines.into_iter().collect::>().join("\n"); write_if_changed(&mod_rs, contents.as_bytes())?; } } Ok(()) }