include_transformed/
lib.rs1#![feature(proc_macro_expand)]
26#![feature(proc_macro_span)]
27
28use std::{io::Read, path::Path, process::Command};
29
30use proc_macro::Literal;
31use quote::quote;
32use syn::{parse_macro_input, LitStr};
33use tempfile::NamedTempFile;
34
35fn simulate_inclusion(file: &LitStr) -> Result<(), proc_macro::ExpandError> {
39 let src = quote! {
40 include_bytes!(#file)
41 };
42 proc_macro::TokenStream::from(src).expand_expr().map(drop)
43}
44
45macro_rules! expand_macro_input {
46 ($tokenstream:ident) => {
47 match $tokenstream.expand_expr() {
48 Ok(tokenstream) => tokenstream,
49 Err(err) => {
50 let tokenstream = proc_macro2::TokenStream::from($tokenstream);
51 let error = syn::Error::new_spanned(tokenstream, err);
52 return proc_macro::TokenStream::from(error.into_compile_error());
53 }
54 }
55 };
56}
57
58fn include_transformed(
59 input: proc_macro::TokenStream,
60 transform: impl FnOnce(&Path, &Path) -> Result<(), proc_macro::TokenStream>,
61) -> proc_macro::TokenStream {
62 let input = expand_macro_input!(input);
63 let file = parse_macro_input!(input as LitStr);
64
65 simulate_inclusion(&file).unwrap();
66 let mut src = proc_macro::Span::call_site().source_file().path();
67 src.set_file_name(file.value());
68 let mut dst = NamedTempFile::new().unwrap();
69
70 if let Err(output) = transform(src.as_path(), dst.path()) {
71 return output;
72 }
73
74 let output = {
75 let mut buf = Vec::new();
76 dst.read_to_end(&mut buf).unwrap();
77 Literal::byte_string(&buf)
78 };
79 proc_macro::TokenStream::from(proc_macro::TokenTree::from(output))
80}
81
82macro_rules! bail {
83 ($($arg:tt)*) => {{
84 let msg = format!($($arg)*);
85 let compile_error = quote! {
86 compile_error!(#msg)
87 };
88 return Err(proc_macro::TokenStream::from(compile_error));
89 }};
90}
91
92fn run(command: &mut Command, program: &str) -> Result<(), proc_macro::TokenStream> {
93 let status = match command.status() {
94 Ok(status) => status,
95 Err(err) => bail!("Failed to execute {program}: {err}"),
96 };
97 if !status.success() {
98 bail!("{program} finished with: {status}");
99 }
100 Ok(())
101}
102
103#[proc_macro]
127pub fn include_nasm_bin(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
128 include_transformed(input, |src, dst| {
129 let program = "nasm";
130 let mut command = Command::new(program);
131 command.arg("-f").arg("bin").arg(src).arg("-o").arg(dst);
132 run(&mut command, program)
133 })
134}