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

192 lines
6.8 KiB
Rust

pub mod types;
pub mod names;
pub mod context;
pub mod parsers;
pub mod unparsers;
pub mod codegen;
use crate::*;
use crate::gen::schema::*;
use crate::syntax::block::Formatter;
use crate::syntax::block::constructors::*;
use glob::glob;
use preserves::value::Map;
use preserves::value::NestedValue;
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,
pub module_aliases: Map<ModulePath, 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(),
module_aliases: Map::new(),
}
}
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.clone()) {
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> {
let modes: Vec<context::ModuleContextMode> =
vec![context::ModuleContextMode::TargetIOValue, context::ModuleContextMode::TargetAny];
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);
m.define_type(item(seq!["pub type _Ptr = ", match &v.embedded_type {
EmbeddedTypeName::False => item("preserves::value::IOValue".to_owned()),
EmbeddedTypeName::Ref(r) => item(seq!["std::sync::Arc", anglebrackets![m.render_ref(&**r)]]),
}, ";"]));
m.define_type(item(seq!["pub type _Any = preserves::value::ArcValue<_Ptr>;"]));
for (n, d) in &v.definitions.0 {
m.define_type(item(types::render_definition_type(&m, n, &types::definition_type(d))));
for mode in &modes {
m.mode = *mode;
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("#![allow(unused_imports)]".to_owned());
lines.push("".to_owned());
lines.push("use std::convert::TryFrom;".to_owned());
lines.push("use preserves::value::Embeddable;".to_owned());
lines.push("use preserves::value::NestedValue;".to_owned());
lines.push(format!("use {}::support as _support;", &config.support_crate));
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(" use preserves::value::NestedValue;".to_owned());
lines.push(" use preserves::value::packed::from_bytes;".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(&value.to_io_value()).unwrap();
lines.push(format!(
" pub static ref {}: {} = /* {:?} */ {}::from_io_value(&from_bytes(&vec!{:?}).unwrap()).unwrap();",
name,
m.target(),
value,
m.target(),
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 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(())
}