126 lines
3.6 KiB
Rust
126 lines
3.6 KiB
Rust
#![feature(proc_macro_value)]
|
|
use std::{fs::File, io::Read};
|
|
|
|
use proc_macro::{Delimiter, Group, Literal, Punct, TokenStream, TokenTree};
|
|
use sha2::Digest;
|
|
|
|
fn read_path_literal(input: TokenStream) -> String {
|
|
let mut input_iter = input.into_iter();
|
|
let path_token = input_iter.next().expect("expected a path");
|
|
assert!(input_iter.next().is_none(), "only expected one argument");
|
|
|
|
let path_literal = match path_token {
|
|
TokenTree::Group(path_group) => {
|
|
let mut path_stream = path_group.stream().into_iter();
|
|
let path_literal = path_stream.next().expect("expected a str literal");
|
|
assert!(path_stream.next().is_none(), "expected a str literal");
|
|
|
|
let TokenTree::Literal(path_literal) = path_literal else {
|
|
panic!("expected a str literal");
|
|
};
|
|
|
|
path_literal
|
|
}
|
|
TokenTree::Literal(path_literal) => path_literal,
|
|
_ => panic!("expected a str literal"),
|
|
};
|
|
|
|
// eprintln!("{path_literal:?}");
|
|
path_literal.str_value().expect("expected a str literal")
|
|
}
|
|
|
|
fn extend_grouped(
|
|
ts: &mut TokenStream,
|
|
delim: Delimiter,
|
|
items: impl Iterator<Item = TokenStream>,
|
|
) {
|
|
let mut comma_ts = TokenStream::new();
|
|
comma_ts.extend(std::iter::once(TokenTree::Punct(Punct::new(
|
|
',',
|
|
proc_macro::Spacing::Alone,
|
|
))));
|
|
|
|
let mut tuple_content = TokenStream::new();
|
|
tuple_content.extend(items.flat_map(|token| [token, comma_ts.clone()]));
|
|
|
|
ts.extend(std::iter::once(TokenTree::Group(Group::new(
|
|
delim,
|
|
tuple_content,
|
|
))));
|
|
}
|
|
|
|
fn extend_array(ts: &mut TokenStream, array: &[u8]) {
|
|
extend_grouped(
|
|
ts,
|
|
Delimiter::Bracket,
|
|
array.iter().map(|&b| {
|
|
let mut ts = TokenStream::new();
|
|
ts.extend(std::iter::once(TokenTree::Literal(Literal::u8_unsuffixed(
|
|
b,
|
|
))));
|
|
ts
|
|
}),
|
|
);
|
|
}
|
|
|
|
fn extend_asset_tuple(ts: &mut TokenStream, buf: &[u8], mime: &str, sha256sum: [u8; 32]) {
|
|
extend_grouped(
|
|
ts,
|
|
Delimiter::Parenthesis,
|
|
[
|
|
{
|
|
let mut ts = TokenStream::new();
|
|
ts.extend(std::iter::once(TokenTree::Punct(Punct::new(
|
|
'&',
|
|
proc_macro::Spacing::Joint,
|
|
))));
|
|
extend_array(&mut ts, buf);
|
|
ts
|
|
},
|
|
{
|
|
let mut ts = TokenStream::new();
|
|
ts.extend(std::iter::once(Literal::string(mime)));
|
|
ts
|
|
},
|
|
{
|
|
let mut ts = TokenStream::new();
|
|
extend_array(&mut ts, &sha256sum);
|
|
ts
|
|
},
|
|
]
|
|
.into_iter(),
|
|
);
|
|
}
|
|
|
|
fn get_mime(buf: &[u8]) -> String {
|
|
let cookie =
|
|
magic::Cookie::open(magic::cookie::Flags::MIME).expect("failure opening mime cookie");
|
|
let cookie = cookie
|
|
.load(&magic::cookie::DatabasePaths::default())
|
|
.expect("failure loading magic db");
|
|
|
|
cookie.buffer(buf).expect("error getting file mime")
|
|
}
|
|
|
|
fn get_sha256sum(buf: &[u8]) -> [u8; 32] {
|
|
sha2::Sha256::digest(buf)
|
|
.as_slice()
|
|
.try_into()
|
|
.expect("failed to sha256sum file")
|
|
}
|
|
|
|
#[proc_macro]
|
|
#[allow(clippy::missing_panics_doc)]
|
|
pub fn include_asset(input: TokenStream) -> TokenStream {
|
|
let path = read_path_literal(input);
|
|
let mut buf = Vec::new();
|
|
File::open(path)
|
|
.expect("couldn't open file")
|
|
.read_to_end(&mut buf)
|
|
.expect("error reading file");
|
|
|
|
let mut ts = TokenStream::new();
|
|
extend_asset_tuple(&mut ts, &buf, &get_mime(&buf), get_sha256sum(&buf));
|
|
ts
|
|
}
|