rust_embed_for_web_impl/
lib.rs1#![recursion_limit = "1024"]
7#![forbid(unsafe_code)]
8#[macro_use]
9extern crate quote;
10extern crate proc_macro;
11
12mod attributes;
13mod compress;
14mod dynamic;
15mod embed;
16
17use attributes::read_attribute_config;
18use dynamic::generate_dynamic_impl;
19use embed::generate_embed_impl;
20use proc_macro::TokenStream;
21use proc_macro2::TokenStream as TokenStream2;
22use std::{env, path::Path};
23use syn::{Data, DeriveInput, Expr, ExprLit, Fields, Lit, Meta, MetaNameValue};
24
25fn find_attribute_values(ast: &syn::DeriveInput, attr_name: &str) -> Vec<String> {
27 ast.attrs
28 .iter()
29 .filter(|value| value.path().is_ident(attr_name))
30 .map(|attr| &attr.meta)
31 .filter_map(|meta| match meta {
32 Meta::NameValue(MetaNameValue {
33 value:
34 Expr::Lit(ExprLit {
35 lit: Lit::Str(val), ..
36 }),
37 ..
38 }) => Some(val.value()),
39 _ => None,
40 })
41 .collect()
42}
43
44fn impl_rust_embed_for_web(ast: &syn::DeriveInput) -> TokenStream2 {
45 match ast.data {
46 Data::Struct(ref data) => match data.fields {
47 Fields::Unit => {}
48 _ => panic!("RustEmbed can only be derived for unit structs"),
49 },
50 _ => panic!("RustEmbed can only be derived for unit structs"),
51 };
52
53 let mut folder_paths = find_attribute_values(ast, "folder");
54 if folder_paths.len() != 1 {
55 panic!("#[derive(RustEmbed)] must contain one and only one folder attribute");
56 }
57 let folder_path = folder_paths.remove(0);
58 #[cfg(feature = "interpolate-folder-path")]
59 let folder_path = shellexpand::full(&folder_path).unwrap().to_string();
60
61 let folder_path = if Path::new(&folder_path).is_relative() {
63 Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap())
64 .join(folder_path)
65 .to_str()
66 .unwrap()
67 .to_owned()
68 } else {
69 folder_path
70 };
71
72 let config = read_attribute_config(ast);
73
74 let prefixes = find_attribute_values(ast, "prefix");
75 let prefix = if prefixes.is_empty() {
76 "".to_string()
77 } else if prefixes.len() == 1 {
78 prefixes[0].clone()
79 } else {
80 panic!("#[derive(RustEmbed)] must have at most one prefix, you supplied several");
81 };
82
83 if cfg!(debug_assertions) && !cfg!(feature = "always-embed") {
84 generate_dynamic_impl(&ast.ident, &config, &folder_path, &prefix)
85 } else {
86 generate_embed_impl(&ast.ident, &config, &folder_path, &prefix)
87 }
88}
89
90#[proc_macro_derive(RustEmbed, attributes(folder, prefix, include, exclude, gzip, br))]
91pub fn derive_input_object(input: TokenStream) -> TokenStream {
107 let ast: DeriveInput = syn::parse(input).unwrap();
108 let gen = impl_rust_embed_for_web(&ast);
109 gen.into()
110}