use std::io::prelude::*;
use std::io::BufReader;
use std::path::{Path, PathBuf};
#[derive(Debug)]
struct BuildError {
message: String,
}
impl std::fmt::Display for BuildError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}
impl std::error::Error for BuildError {}
impl From<String> for BuildError {
fn from(x: String) -> Self {
Self {
message: x.to_string(),
}
}
}
impl From<&str> for BuildError {
fn from(x: &str) -> Self {
Self {
message: x.to_string(),
}
}
}
fn cargo_expand(crate_dir: &Path, module_path: &str) -> Result<Vec<u8>, BuildError> {
use std::process::Command;
let output = Command::new("cargo")
.arg("expand")
.arg("--lib")
.arg(module_path)
.current_dir(crate_dir)
.output()
.expect("Failed to execute `cargo-expand`");
if !output.status.success() {
#[cfg(debug_assertions)]
println!(
"{}",
std::str::from_utf8(&output.stdout).unwrap_or_default()
);
#[cfg(debug_assertions)]
println!(
"{}",
std::str::from_utf8(&output.stderr).unwrap_or_default()
);
Err("`cargo-expand` returned failed. Aborted.")?;
}
Ok(output.stdout)
}
fn update_cbindgen_bindings(crate_dir: &Path) -> Result<(), BuildError> {
let input_path = {
let mut p = PathBuf::new();
p.push(crate_dir);
p.push("cbindgen");
p.push("src");
p
};
let output_path = {
let mut p = PathBuf::new();
p.push(crate_dir);
p.push("float_pigment_css.h");
p
};
{
let mut src_path = PathBuf::new();
src_path.push(crate_dir);
src_path.push("src");
let copy_options = fs_extra::dir::CopyOptions {
overwrite: true,
content_only: true,
..Default::default()
};
fs_extra::dir::copy(&src_path, &input_path, ©_options).unwrap();
}
let mut property_expand_result = cargo_expand(crate_dir, "property")?;
let typing_expand_result = cargo_expand(crate_dir, "typing")?;
let mut extra_content = {
let mut extra_path = PathBuf::new();
extra_path.push(crate_dir);
extra_path.push("cbindgen");
extra_path.push("extra.rs");
std::fs::read(&extra_path).unwrap()
};
{
let mut property_content = vec![];
property_content.append(&mut extra_content);
property_content.append(&mut property_expand_result);
let mut property_path = PathBuf::new();
property_path.push(&input_path);
property_path.push("property.rs");
std::fs::write(&property_path, property_content).unwrap();
}
{
let mut typing_path = PathBuf::new();
typing_path.push(&input_path);
typing_path.push("typing.rs");
std::fs::write(&typing_path, typing_expand_result).unwrap();
}
let extra_header = {
let mut extra_path = PathBuf::new();
extra_path.push(crate_dir);
extra_path.push("cbindgen");
extra_path.push("extra_header.h");
std::fs::read(&extra_path).unwrap()
};
{
use cbindgen::{Builder, Config};
let bindings = Builder::new()
.with_config(Config::from_file(&{
let mut p = PathBuf::new();
p.push(crate_dir);
p.push("cbindgen");
p.push("cbindgen.toml");
p
})?)
.with_src(&{
let mut p = PathBuf::new();
p.push(input_path);
p.push("lib.rs");
p
})
.generate()
.map_err(|x| x.to_string())?;
let mut output_file = std::fs::File::create(&output_path).map_err(|x| x.to_string())?;
output_file
.write(&extra_header)
.map_err(|x| x.to_string())?;
bindings.write(&mut output_file);
let file = std::fs::File::open(&output_path).unwrap();
let extra_struct = {
let mut path = PathBuf::new();
path.push(crate_dir);
path.push("cbindgen");
path.push("extra_struct.h");
std::fs::read_to_string(&path).unwrap()
};
let reader = BufReader::new(file);
let mut a: Vec<String> = vec![];
for line in reader.lines() {
let line = line.unwrap();
match line.trim() {
"namespace float_pigment {" => {
a.push("namespace float_pigment {".into());
a.push(extra_struct.clone());
}
_ => {
a.push(line.replace(">>", "> >"));
}
}
}
let mut s = a.join("\n").to_string();
s.push('\n');
let mut file = std::fs::File::create(&output_path).unwrap();
file.write_all(s.as_bytes()).expect("Write Error");
}
Ok(())
}
fn main() -> Result<(), BuildError> {
if let Ok(crate_dir) = std::env::var("CARGO_MANIFEST_DIR") {
update_cbindgen_bindings(Path::new(&crate_dir))?;
} else {
Err("CARGO_MANIFEST_DIR is not set. Skipped cbindgen step.")?;
}
Ok(())
}