1use proc_macro::{TokenStream, TokenTree};
6use std::str::FromStr;
7
8#[proc_macro]
28pub fn blob(input: TokenStream) -> TokenStream {
29 let tokens: Vec<TokenTree> = input.into_iter().collect();
30
31 let static_idx = tokens
32 .iter()
33 .position(|t| matches!(t, TokenTree::Ident(i) if i.to_string() == "static"))
34 .expect("inline-blob: expected `static` keyword (e.g. `pub static NAME, \"path\"`)");
35
36 let vis_stream: TokenStream = tokens[..static_idx].iter().cloned().collect();
37 let vis_str = vis_stream.to_string();
38
39 let after = &tokens[static_idx + 1..];
40
41 let name = match after.first() {
42 Some(TokenTree::Ident(i)) => i.to_string(),
43 _ => panic!("inline-blob: expected identifier after `static`"),
44 };
45
46 match after.get(1) {
47 Some(TokenTree::Punct(p)) if p.as_char() == ',' => {}
48 _ => panic!("inline-blob: expected `,` after identifier"),
49 }
50
51 let path_tokens: TokenStream = after[2..].iter().cloned().collect();
52 if path_tokens.is_empty() {
53 panic!("inline-blob: expected path expression after `,`");
54 }
55 let path_expr = path_tokens.to_string();
56
57 let anchor_section = format!(".lbss.{}", name.to_lowercase());
58 let section = format!(".lrodata.{}", name.to_lowercase());
59
60 let generated = format!(
61 "#[used] \
62 #[unsafe(link_section = \"{anchor_section}\")] \
63 static __ANCHOR_{name}: [u8; 1] = [0];
64 #[used] \
65 #[unsafe(link_section = \"{section}\")] \
66 {vis_str} static {name}: [u8; const {{ ::core::include_bytes!({path_expr}).len() }}] \
67 = *::core::include_bytes!({path_expr});"
68 );
69
70 TokenStream::from_str(&generated)
71 .expect("inline-blob: failed to construct output token stream")
72}