use std::{
fs::File,
io::Write,
path::{Path, PathBuf},
};
use zngur_generator::{
ParsedZngFile, ZngHeaderGenerator, ZngurGenerator,
cfg::{InMemoryRustCfgProvider, NullCfg, RustCfgProvider},
};
#[must_use]
pub struct Zngur {
zng_file: PathBuf,
h_file_path: Option<PathBuf>,
cpp_file_path: Option<PathBuf>,
rs_file_path: Option<PathBuf>,
depfile_path: Option<PathBuf>,
mangling_base: Option<String>,
cpp_namespace: Option<String>,
rust_cfg: Option<Box<dyn RustCfgProvider>>,
zng_header_in_place: bool,
zng_h_file_path: Option<PathBuf>,
crate_name: Option<String>,
}
impl Zngur {
pub fn from_zng_file(zng_file_path: impl AsRef<Path>) -> Self {
Zngur {
zng_file: zng_file_path.as_ref().to_owned(),
h_file_path: None,
cpp_file_path: None,
rs_file_path: None,
depfile_path: None,
mangling_base: None,
cpp_namespace: None,
rust_cfg: None,
zng_header_in_place: false,
zng_h_file_path: None,
crate_name: None,
}
}
pub fn with_zng_header(mut self, zng_header: impl AsRef<Path>) -> Self {
self.zng_h_file_path.replace(zng_header.as_ref().to_owned());
self
}
pub fn with_h_file(mut self, path: impl AsRef<Path>) -> Self {
self.h_file_path = Some(path.as_ref().to_owned());
self
}
pub fn with_cpp_file(mut self, path: impl AsRef<Path>) -> Self {
self.cpp_file_path = Some(path.as_ref().to_owned());
self
}
pub fn with_rs_file(mut self, path: impl AsRef<Path>) -> Self {
self.rs_file_path = Some(path.as_ref().to_owned());
self
}
pub fn with_depfile(mut self, path: impl AsRef<Path>) -> Self {
self.depfile_path = Some(path.as_ref().to_owned());
self
}
pub fn with_mangling_base(mut self, mangling_base: &str) -> Self {
self.mangling_base = Some(mangling_base.to_owned());
self
}
pub fn with_cpp_namespace(mut self, cpp_namespace: &str) -> Self {
self.cpp_namespace = Some(cpp_namespace.to_owned());
self
}
pub fn with_crate_name(mut self, crate_name: &str) -> Self {
self.crate_name = Some(crate_name.to_owned());
self
}
pub fn with_rust_cargo_cfg(mut self) -> Self {
self.rust_cfg = Some(Box::new(
InMemoryRustCfgProvider::default().load_from_cargo_env(),
));
self
}
pub fn with_zng_header_in_place_as(mut self, value: bool) -> Self {
self.zng_header_in_place = value;
self
}
pub fn with_zng_header_in_place(self) -> Self {
self.with_zng_header_in_place_as(true)
}
pub fn with_rust_in_memory_cfg<'a, CfgPairs, CfgKey, CfgValues>(
mut self,
cfg_values: CfgPairs,
) -> Self
where
CfgPairs: IntoIterator<Item = (CfgKey, CfgValues)>,
CfgKey: AsRef<str> + 'a,
CfgValues: Clone + IntoIterator + 'a,
<CfgValues as IntoIterator>::Item: AsRef<str>,
{
self.rust_cfg = Some(Box::new(
InMemoryRustCfgProvider::default().with_values(cfg_values),
));
self
}
pub fn generate(self) {
let rust_cfg = self.rust_cfg.unwrap_or_else(|| Box::new(NullCfg));
let parse_result = ParsedZngFile::parse(self.zng_file, rust_cfg);
let crate_name = self
.crate_name
.or_else(|| std::env::var("CARGO_PKG_NAME").ok())
.unwrap_or_else(|| "crate".to_owned());
let panic_to_exception = parse_result.spec.convert_panic_to_exception.0;
let mut file = ZngurGenerator::build_from_zng(parse_result.spec, crate_name);
let rs_file_path = self.rs_file_path.expect("No rs file path provided");
let h_file_path = self.h_file_path.expect("No h file path provided");
file.0.cpp_include_header_name = h_file_path
.file_name()
.unwrap()
.to_string_lossy()
.into_owned();
if let Some(cpp_namespace) = &self.cpp_namespace {
file.0.mangling_base = cpp_namespace.clone();
file.0.cpp_namespace = Some(cpp_namespace.clone());
}
if let Some(mangling_base) = self.mangling_base.or_else(|| file.0.cpp_namespace.clone()) {
file.0.mangling_base = mangling_base;
}
let (rust, h, cpp) = file.render(self.zng_header_in_place);
File::create(&rs_file_path)
.unwrap()
.write_all(rust.as_bytes())
.unwrap();
File::create(&h_file_path)
.unwrap()
.write_all(h.as_bytes())
.unwrap();
if let Some(cpp) = &cpp {
let cpp_file_path = self
.cpp_file_path
.as_ref()
.expect("No cpp file path provided");
File::create(cpp_file_path)
.unwrap()
.write_all(cpp.as_bytes())
.unwrap();
}
if let Some(depfile_path) = self.depfile_path {
let mut targets = vec![
h_file_path.display().to_string(),
rs_file_path.display().to_string(),
];
if let Some(cpp_path) = self.cpp_file_path {
if cpp.is_some() {
targets.push(cpp_path.display().to_string());
}
}
let deps: Vec<String> = parse_result
.processed_files
.iter()
.map(|p| p.display().to_string())
.collect();
let depfile_content = format!("{}: {}\n", targets.join(" "), deps.join(" "));
File::create(depfile_path)
.unwrap()
.write_all(depfile_content.as_bytes())
.unwrap();
}
if let Some(zng_h) = self.zng_h_file_path {
let mut zng = ZngurHdr::new()
.with_panic_to_exception_as(panic_to_exception)
.with_zng_header(zng_h);
if let Some(cpp_namespace) = &self.cpp_namespace {
zng = zng.with_cpp_namespace(&cpp_namespace);
}
zng.generate();
}
}
}
#[derive(Debug)]
pub struct ZngurHdr {
panic_to_exception: bool,
zng_header_file: Option<PathBuf>,
cpp_namespace: Option<String>,
}
impl ZngurHdr {
pub const fn new() -> Self {
Self {
panic_to_exception: false,
zng_header_file: None,
cpp_namespace: None,
}
}
pub fn with_panic_to_exception(self) -> Self {
self.with_panic_to_exception_as(true)
}
pub fn without_panic_to_exception(self) -> Self {
self.with_panic_to_exception_as(false)
}
pub fn with_panic_to_exception_as(mut self, panic_to_exception: bool) -> Self {
self.panic_to_exception = panic_to_exception;
self
}
pub fn with_zng_header(mut self, zng_header: impl Into<PathBuf>) -> Self {
self.zng_header_file.replace(zng_header.into());
self
}
pub fn with_cpp_namespace(mut self, cpp_namespace: &str) -> Self {
self.cpp_namespace = Some(cpp_namespace.to_owned());
self
}
pub fn generate(self) {
let generator = ZngHeaderGenerator {
panic_to_exception: self.panic_to_exception,
cpp_namespace: self.cpp_namespace.unwrap_or_else(|| "rust".to_owned()),
};
let out_h = self
.zng_header_file
.expect("Missing zng header output file");
let rendered = generator.render();
std::fs::write(&out_h, rendered)
.unwrap_or_else(|_| panic!("Couldn't write contents to {}", out_h.display()));
}
}