preserves/implementations/rust/preserves-schema/src/compiler/mod.rs

163 lines
5.3 KiB
Rust

pub mod types;
pub mod names;
pub mod context;
pub mod parsers;
pub mod unparsers;
pub mod codegen;
use crate::gen::schema::*;
use crate::syntax::block::Formatter;
use crate::syntax::block::constructors::*;
use glob::glob;
use preserves::value::Map;
use preserves::value::PackedReader;
use preserves::value::Reader;
use preserves::value::Set;
use std::convert::TryFrom;
use std::fs::DirBuilder;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Error;
use std::io::ErrorKind;
use std::io::Write;
use std::path::PathBuf;
pub type ModulePath = Vec<String>;
#[derive(Debug)]
pub struct CompilerConfig {
pub bundle: Map<ModulePath, Schema>,
pub output_dir: PathBuf,
pub fully_qualified_module_prefix: String,
pub support_crate: String,
}
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(),
}
}
pub fn load_schemas_and_bundles(&mut self, inputs: &Vec<PathBuf>) -> Result<(), Error> {
for i in inputs {
let mut f = File::open(&i)?;
let mut reader = PackedReader::decode_read(&mut f);
let blob = reader.demand_next(false)?;
if let Ok(s) = Schema::try_from(&blob) {
let prefix = i.file_stem().ok_or_else(
|| Error::new(ErrorKind::InvalidData, format!("Bad schema file stem: {:?}", i)))?
.to_str().ok_or_else(
|| Error::new(ErrorKind::InvalidData, format!("Invalid UTF-8 in schema file name: {:?}", i)))?;
self.bundle.insert(vec![prefix.to_owned()], s);
return Ok(());
}
if let Ok(Bundle { modules }) = Bundle::try_from(&blob) {
for (ModulePath(k), v) in modules.0 {
self.bundle.insert(k, v);
}
return Ok(());
}
return Err(Error::new(ErrorKind::InvalidData,
format!("Invalid schema binary blob {:?}", i)));
}
Ok(())
}
}
pub fn expand_inputs(globs: &Vec<String>) -> Result<Vec<PathBuf>, Error> {
let mut result = Vec::new();
for g in globs.iter() {
for p in glob(g).map_err(|e| Error::new(ErrorKind::InvalidData, format!("{}", e)))? {
result.push(p.map_err(glob::GlobError::into_error)?)
}
}
Ok(result)
}
pub fn compile(config: &CompilerConfig) -> Result<(), std::io::Error> {
for (k, v) in config.bundle.iter() {
let mut output_path = config.output_dir.clone();
output_path.extend(k);
output_path.set_extension("rs");
let module_name = output_path.file_stem().unwrap().to_str().unwrap().to_owned();
DirBuilder::new().recursive(true).create(output_path.parent().unwrap())?;
let mut m = context::ModuleContext::new(config);
// TODO: embedded type
for (n, d) in &v.definitions.0 {
m.define_type(item(types::render_definition_type(&m, n, &types::definition_type(d))));
parsers::gen_definition_parser(&mut m, n, d);
unparsers::gen_definition_unparser(&mut m, n, d);
}
//---------------------------------------------------------------------------
let mut lines: Vec<String> = Vec::new();
lines.push("#![allow(unused_parens)]".to_owned());
lines.push("".to_owned());
lines.push("use std::convert::TryFrom;".to_owned());
lines.push("use preserves::value::NestedValue;".to_owned());
lines.push(format!("use {}::support::*;", &config.support_crate));
lines.push("".to_owned());
lines.push("lazy_static! {".to_owned());
for (value, name) in m.literals {
let bs = preserves::value::PackedWriter::encode(&value).unwrap();
lines.push(format!(" pub static ref {}: preserves::value::IOValue = /* {:?} */ preserves::value::packed::from_bytes(&vec!{:?}).unwrap();",
name,
value,
bs));
}
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 mut f = File::create(&output_path)?;
for line in lines {
write!(f, "{}\n", line)?;
}
}
{
let mut mod_rs = output_path.clone();
mod_rs.set_file_name("mod.rs");
let mut lines = if !mod_rs.exists() {
Set::new()
} else {
BufReader::new(File::open(&mod_rs)?)
.lines().collect::<Result<Set<String>, Error>>()?
};
lines.insert(format!("pub mod {};", &module_name));
let mut f = File::create(mod_rs)?;
for line in lines {
write!(f, "{}\n", line)?;
}
}
}
Ok(())
}