include_blob_macros/
lib.rs

1//! Private, semver-unstable implementation details of the `include-blob` crate.
2//!
3//! Don't use this crate, use `include-blob` instead.
4
5use std::{
6    collections::hash_map::DefaultHasher,
7    env, fs,
8    hash::{Hash, Hasher},
9    path::PathBuf,
10    str::FromStr,
11};
12
13use proc_macro::TokenStream;
14
15/// Includes a binary file that was prepared for inclusion by a build script.
16///
17/// Takes a string literal as its argument, denoting the file's path (relative to the directory
18/// containing the package's `Cargo.toml`).
19///
20/// The macro expands to an expression of type `&[u8; N]`, where `N` is the size of the file in
21/// Bytes.
22#[proc_macro]
23pub fn include_blob(args: TokenStream) -> TokenStream {
24    let lit: syn::LitStr = syn::parse(args).unwrap();
25    let lit = lit.value();
26
27    let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
28    path.push(lit);
29
30    let path = path.canonicalize().unwrap_or_else(|_| {
31        panic!("could not find file '{}'", path.display(),);
32    });
33    let metadata = fs::metadata(&path).unwrap();
34    assert!(metadata.is_file());
35    let len = metadata.len();
36
37    let mut hasher = DefaultHasher::new();
38    path.hash(&mut hasher);
39    metadata.modified().unwrap().hash(&mut hasher);
40    let unique_name = format!("include_blob_{:016x}", hasher.finish());
41
42    TokenStream::from_str(&format!(
43        r#"
44        {{
45            extern "C" {{
46                #[link_name = "{unique_name}"]
47                static STATIC: [u8; {len}];
48            }}
49            unsafe {{ &STATIC }}
50        }}
51        "#
52    ))
53    .unwrap()
54}