1use std::{
2 sync::atomic::AtomicUsize,
3 time::SystemTime,
4 fs::File,
5 io::Write,
6 process::Command,
7 str::FromStr
8};
9
10use syn::{
11 parse::Parser,
12 punctuated::Punctuated,
13 spanned::Spanned,
14 Token
15};
16
17use proc_macro::TokenStream;
18use quote::ToTokens;
19use sha2::Digest;
20
21mod storage;
22use storage::{StorageDir, TargetDir};
23
24mod cargo;
25mod rustc;
26
27enum InlineRustError {
28 CargoError(String),
29 RustcError(String),
30 RuntimeError(String),
31 Other(Box<dyn std::error::Error>),
32}
33impl<E: std::error::Error + 'static> From<E> for InlineRustError {
34 fn from(err: E) -> Self {
35 InlineRustError::Other(Box::new(err))
36 }
37}
38impl Into<TokenStream> for InlineRustError {
39 fn into(self) -> TokenStream {
40 let str = match self {
41 InlineRustError::RuntimeError(str)
42 | InlineRustError::CargoError(str)
43 | InlineRustError::RustcError(str) => str,
44 InlineRustError::Other(err) => err.to_string(),
45 };
46
47 syn::Error::new(str.span(), str).to_compile_error().into()
48 }
49}
50
51fn exec_id(code: &str) -> String {
52 static INVOKE_ID: AtomicUsize = AtomicUsize::new(0);
53
54 let invoke_id = INVOKE_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
55
56 let mut sha256 = sha2::Sha256::new();
57
58 sha256.update(&invoke_id.to_ne_bytes());
59
60 if let Ok(systime) = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
61 sha256.update(&systime.as_nanos().to_ne_bytes());
62 }
63
64 sha256.update(code.as_bytes());
65
66 format!("inline_rust_{:x}", sha256.finalize())[0..32].to_string()
67}
68
69#[proc_macro]
70pub fn inline_rust(tokens: TokenStream) -> TokenStream {
104 let parser = Punctuated::<syn::Expr, Token![,]>::parse_separated_nonempty;
105 let mut parsed = match parser.parse(tokens) {
106 Ok(parsed) => parsed,
107 Err(error) => return error.into_compile_error().into(),
108 };
109
110 let code = match parsed.pop() {
111 Some(code) => code.into_value().into_token_stream().to_string(),
112 None => return TokenStream::default(),
113 };
114
115 let manifest = match parsed.pop().map(|pair| pair.into_value()) {
116 Some(manifest) => loop {
117 if let syn::Expr::Lit(ref str) = manifest {
118 if let syn::Lit::Str(ref str) = str.lit {
119 break Some(str.value());
120 }
121 }
122 return syn::Error::new(
123 manifest.span(),
124 "Expected string literal for Cargo manifest",
125 )
126 .to_compile_error()
127 .into();
128 },
129 None => None,
130 };
131
132 let code = format!("fn inline_rust() -> impl std::fmt::Display {{\n{}\n}} fn main() {{println!(\"{{}}\", inline_rust())}}", code.trim());
133
134 let exec_id = exec_id(&code);
135 let storage_dir = storage::StorageDir::create(&exec_id).expect("Failed to create storage directory");
136
137 let result = if let Some(manifest) = manifest {
138 cargo::try_inline(storage_dir.target_dir(exec_id).expect("Failed to create Cargo target directory"), storage_dir, manifest.trim(), &code)
139 } else {
140 rustc::try_inline(storage_dir, &code)
141 };
142
143 match result {
144 Ok(tokens) => tokens,
145 Err(err) => err.into(),
146 }
147}