css_modules_macros/
lib.rs

1#![feature(proc_macro_span)]
2extern crate proc_macro;
3
4use proc_macro::TokenStream;
5use quote::quote;
6use syn::{parse_macro_input, LitStr};
7use std::env;
8use std::path::{Path, PathBuf};
9
10#[proc_macro]
11pub fn include_css_module(input: TokenStream) -> TokenStream {
12    if input.is_empty() {
13        parse_macro_input!(input as LitStr);
14        unreachable!();
15    }
16
17    let out_dir = env::var("OUT_DIR")
18        .expect("Please set up CSS Modules in your build script.");
19    let out_dir = Path::new(&out_dir);
20
21    let input_path = input.clone();
22    let input_path = parse_macro_input!(input_path as LitStr).value();
23
24    let mut path = out_dir.join(
25        strip_manifest_dir(
26            replace_file_name(
27                token_stream_source_file(input),
28                input_path
29            )
30        )
31    );
32
33    if let Some(extension) = path.extension() {
34        path = path.with_extension(format!("{}.rs", extension.to_str().unwrap()));
35    } else {
36        path = path.with_extension("rs");
37    }
38
39    let path = path.to_str();
40
41    TokenStream::from(quote! {
42        {
43            use css_modules::CssModuleBuilder;
44
45            include!(#path)
46        }
47    })
48}
49
50fn token_stream_source_file(input: TokenStream) -> PathBuf {
51    let file = input.into_iter().next().unwrap().span().source_file();
52
53    if !file.is_real() {
54        panic!("Must be called from a file.");
55    }
56
57    file.path().canonicalize().unwrap()
58}
59
60fn strip_manifest_dir(path: PathBuf) -> PathBuf {
61    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
62
63    path.strip_prefix(manifest_dir).unwrap().into()
64}
65
66fn strip_file_name(path: PathBuf) -> PathBuf {
67    path.parent().unwrap().into()
68}
69
70fn replace_file_name(path: PathBuf, file_name: String) -> PathBuf {
71    strip_file_name(path).join(file_name)
72}