static_file_http_server_macros/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::{TokenStream, TokenTree};
4use std::{env, fmt::Display, fs, path::PathBuf};
5
6fn git_sbp_url(sub_uri: &impl ToString) -> String {
7    format!(
8        "https://raw.githubusercontent.com/devraymondsh/static-file-http-server/v{}/single-binary-producer/{}",
9        env::var("CARGO_PKG_VERSION").unwrap(),
10        sub_uri.to_string()
11    )
12}
13fn download_from_repo(sub_uri: &(impl ToString + Display)) -> String {
14    String::from_utf8_lossy(
15        &reqwest::blocking::get(git_sbp_url(sub_uri))
16            .unwrap()
17            .bytes()
18            .unwrap(),
19    )
20    .to_string()
21}
22
23const MAIN_RS_ORIGINAL_PATH: &str = concat!(
24    env!("CARGO_MANIFEST_DIR"),
25    "/../single-binary-producer/src/main.rs"
26);
27const CARGO_TOML_ORIGINAL_PATH: &str = concat!(
28    env!("CARGO_MANIFEST_DIR"),
29    "/../single-binary-producer/Cargo.toml"
30);
31
32fn get_main_rs_path() -> String {
33    if PathBuf::from(MAIN_RS_ORIGINAL_PATH).exists() {
34        let main_rs_contents = fs::read_to_string(MAIN_RS_ORIGINAL_PATH).unwrap();
35        main_rs_contents
36    } else {
37        download_from_repo(&"src/main.rs")
38    }
39}
40fn get_cargo_toml_path() -> String {
41    if PathBuf::from(CARGO_TOML_ORIGINAL_PATH).exists() {
42        let cargo_toml_contents = fs::read_to_string(CARGO_TOML_ORIGINAL_PATH).unwrap();
43        cargo_toml_contents
44    } else {
45        download_from_repo(&"Cargo.toml")
46    }
47}
48fn quote(name: impl ToString, contents: impl ToString) -> TokenStream {
49    format!(
50        "const {}: &str = r#\"{}\"#;",
51        name.to_string(),
52        contents.to_string()
53    )
54    .parse()
55    .unwrap()
56}
57
58fn parse_input(input: TokenStream) -> String {
59    match &input.into_iter().collect::<Vec<_>>()[0] {
60        TokenTree::Literal(lit) => {
61            let mut repr = lit.to_string();
62            if !repr.starts_with('"') || !repr.ends_with('"') {
63                panic!("This macro only accepts a single, non-empty string argument")
64            }
65
66            repr.remove(0);
67            repr.pop();
68
69            repr
70        }
71        _ => panic!("This macro only accepts a single, non-empty string argument"),
72    }
73}
74
75#[proc_macro]
76pub fn get_sbp_main_rs(input: TokenStream) -> TokenStream {
77    let contents = get_main_rs_path();
78    let name = parse_input(input);
79
80    quote(name, contents)
81}
82
83#[proc_macro]
84pub fn get_sbp_cargo_toml(input: TokenStream) -> TokenStream {
85    let contents = get_cargo_toml_path();
86    let name = parse_input(input);
87
88    quote(name, contents)
89}