use std::{
env, io,
path::{Path, PathBuf},
process::Command,
};
pub fn generate(
schemas_path: impl AsRef<Path>,
out_dir: impl AsRef<Path>,
) -> Result<(), GeneratorError> {
let schemas_path = schemas_path.as_ref();
let out_dir = out_dir.as_ref();
eprintln!("creating python virtualenv");
let _ = Command::new("python3")
.arg("-m")
.arg("venv")
.arg(out_dir)
.spawn()
.map_err(GeneratorError::PythonError)?
.wait()?;
let generator_path = if let Ok(path) = env::var("FLATDATA_GENERATOR_PATH") {
let path = PathBuf::from(path).canonicalize()?;
println!("cargo:rerun-if-changed={}", path.display());
eprintln!("installing flatdata-generator from source");
path
} else {
eprintln!("installing flatdata-generator from PyPI");
PathBuf::from("flatdata-generator==0.4.5")
};
let _ = Command::new(out_dir.join("bin/pip3"))
.arg("install")
.arg("--disable-pip-version-check")
.arg(&generator_path)
.spawn()
.map_err(GeneratorError::PythonError)?
.wait()?;
for entry in walkdir::WalkDir::new(&schemas_path) {
let entry = entry?;
if entry.path().extension().map_or(true, |x| x != "flatdata") {
continue;
}
let result: PathBuf = if schemas_path.is_dir() {
out_dir
.join(entry.path().strip_prefix(&schemas_path).unwrap())
.with_extension("rs")
} else {
out_dir
.join(entry.path().file_name().unwrap())
.with_extension("rs")
};
eprintln!(
"generating {} from {}",
result.display(),
schemas_path.display()
);
std::fs::create_dir_all(result.parent().unwrap())?;
let exit_code = Command::new(out_dir.join("bin/flatdata-generator"))
.arg("-g")
.arg("rust")
.arg("-s")
.arg(&entry.path())
.arg("-O")
.arg(&result)
.spawn()
.map_err(|e| GeneratorError::Failure {
schema: entry.path().into(),
destination: result.clone(),
error: e,
})?
.wait()?
.code();
if exit_code != Some(0) {
return Err(GeneratorError::Failure {
schema: entry.path().into(),
destination: result,
error: io::Error::new(
io::ErrorKind::Other,
match exit_code {
Some(code) => format!("Failed to run the generator, exit code {}", code),
None => "Failed to run the generator, no exit code".into(),
},
),
});
}
println!("cargo:rerun-if-changed={}", entry.path().display());
}
Ok(())
}
#[derive(Debug)]
pub enum GeneratorError {
PythonError(std::io::Error),
Io(std::io::Error),
Failure {
schema: PathBuf,
destination: PathBuf,
error: std::io::Error,
},
}
impl std::fmt::Display for GeneratorError {
fn fmt(self: &Self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match self {
GeneratorError::PythonError(err) => {
writeln!(f, "{} could not be executed", err)?;
writeln!(
f,
"Failed to prepare virtualenv for flatdata-generator: please make sure both python3 and python3-virtualenv are installed."
)
}
GeneratorError::Io(details) => {
write!(f, "Failed to run flatdata-generator: {}", details)
}
GeneratorError::Failure {
schema,
destination,
error,
} => write!(
f,
"Failed to run generate {} from {}: {}",
schema.display(),
destination.display(),
error
),
}
}
}
impl std::error::Error for GeneratorError {}
impl std::convert::From<std::io::Error> for GeneratorError {
fn from(detail: std::io::Error) -> Self {
GeneratorError::Io(detail)
}
}
impl std::convert::From<walkdir::Error> for GeneratorError {
fn from(detail: walkdir::Error) -> Self {
GeneratorError::Io(detail.into())
}
}