use std::{error::Error, fs::File, io::Write, path::Path};
use crate::extract::Extraction;
pub fn generate(extraction: Extraction, dest: &Path) -> Result<(), Box<dyn Error>> {
let out_bin = dest.join(format!(
"{}_stencils.bin",
extraction.lib_name.to_lowercase()
));
{
let mut out_bin = File::options()
.create(true)
.truncate(true)
.write(true)
.open(&out_bin)?;
out_bin.write_all(&extraction.all_code)?;
}
{
let out_rs = dest.join(format!(
"{}_stencils.rs",
extraction.lib_name.to_lowercase()
));
let mut out_rs = File::options()
.create(true)
.truncate(true)
.write(true)
.open(&out_rs)?;
let lib_upper = extraction.lib_name.to_uppercase();
out_rs.write_all(
r#"/// Generated by patchouly-build
mod stencils {
use patchouly_core::StencilLibrary;
use patchouly_core::stencils::{Stencil, StencilFamily};
use patchouly_core::relocation::Relocation;
"#
.as_bytes(),
)?;
out_rs.write_fmt(format_args!(
r#"
pub const {}_STENCIL_LIBRARY: StencilLibrary<{}> = StencilLibrary {{
code: include_bytes!({}),
empty: b"{}",
moves: &{}___MOVE,
}};"#,
lib_upper,
extraction.max_regs,
escape_string(&out_bin.display().to_string()),
extraction
.families
.get("__empty")
.and_then(|empty| {
if empty.stencils.len() != 1 {
return None;
}
let stencil = empty.stencils[0];
Some(escape_binary(stencil.code(&extraction.all_code)))
})
.unwrap_or("".to_string()),
lib_upper,
))?;
let endian = cfg!(target_endian = "little");
out_rs.write_fmt(format_args!(
r#"
// endianess assertion
const _: [u8; 1] = [0; (cfg!(target_endian = "little") == {}) as usize];"#,
endian
))?;
for (name, stencil) in extraction.families {
if name == "__empty" {
continue;
}
out_rs.write_fmt(format_args!(
r#"
pub const {}_{}: StencilFamily<{}, {}, {}, {}, {}> = StencilFamily {{
relocation_data: &["#,
lib_upper,
name.to_uppercase(),
stencil.IN,
stencil.OUT,
stencil.MAX_REGS,
stencil.HOLES,
stencil.JUMPS,
))?;
for reloc in &stencil.relocation_data {
out_rs.write_fmt(format_args!(
r#"
Relocation::from_bits(0x{:x}),"#,
reloc.into_bits()
))?;
}
out_rs.write_all(
r#"
],
stencils: &["#
.as_bytes(),
)?;
for stencil in &stencil.stencils {
out_rs.write_fmt(format_args!(
r#"
Stencil::from_bits(0x{:x}),"#,
stencil.into_bits()
))?;
}
out_rs.write_all(
r#"
],
};
"#
.as_bytes(),
)?;
}
out_rs.write_all(b"} // mod stencils\n")?;
}
Ok(())
}
fn escape_string(s: &str) -> String {
let hashes = "#".repeat(s.chars().filter(|c| *c == '#').count() + 1);
format!("r{}\"{}\"{}", hashes, s, hashes)
}
fn escape_binary(s: &[u8]) -> String {
s.iter().map(|b| format!("\\x{:02x}", b)).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_escape_string() {
assert_eq!(r#####"r##"foo#bar"##"#####, escape_string("foo#bar"));
}
#[test]
fn test_escape_binary() {
assert_eq!(r#"\x00\x01\x02"#, escape_binary(&[0, 1, 2]));
}
}