use std::{
fs::File,
io::Write,
path::{Path, PathBuf},
};
use zngur_generator::{
ParsedZngFile, 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>>,
}
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,
}
}
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_rust_cargo_cfg(mut self) -> Self {
self.rust_cfg = Some(Box::new(
InMemoryRustCfgProvider::default().load_from_cargo_env(),
));
self
}
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 mut file = ZngurGenerator::build_from_zng(parse_result.spec);
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();
file.0.cpp_namespace = "rust".to_owned();
if let Some(cpp_namespace) = self.cpp_namespace {
file.0.mangling_base = cpp_namespace.clone();
file.0.cpp_namespace = cpp_namespace;
}
if let Some(mangling_base) = self.mangling_base {
file.0.mangling_base = mangling_base;
}
let cpp_namespace = file.0.cpp_namespace.clone();
let (rust, mut h, mut cpp) = file.render();
h = h
.replace("rust::", &format!("{cpp_namespace}::"))
.replace("namespace rust", &format!("namespace {cpp_namespace}"));
cpp = cpp.map(|cpp| cpp.replace("rust::", &format!("{cpp_namespace}::")));
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();
}
}
}