css-modules-macros 0.5.2

CSS Modules with a macro for convenience.
Documentation
#![feature(proc_macro_span)]
extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, LitStr};
use std::env;
use std::path::{Path, PathBuf};

#[proc_macro]
pub fn include_css_module(input: TokenStream) -> TokenStream {
    if input.is_empty() {
        parse_macro_input!(input as LitStr);
        unreachable!();
    }

    let out_dir = env::var("OUT_DIR")
        .expect("Please set up CSS Modules in your build script.");
    let out_dir = Path::new(&out_dir);

    let input_path = input.clone();
    let input_path = parse_macro_input!(input_path as LitStr).value();

    let mut path = out_dir.join(
        strip_manifest_dir(
            replace_file_name(
                token_stream_source_file(input),
                input_path
            )
        )
    );

    if let Some(extension) = path.extension() {
        path = path.with_extension(format!("{}.rs", extension.to_str().unwrap()));
    } else {
        path = path.with_extension("rs");
    }

    let path = path.to_str();

    TokenStream::from(quote! {
        {
            use css_modules::CssModuleBuilder;

            include!(#path)
        }
    })
}

fn token_stream_source_file(input: TokenStream) -> PathBuf {
    let file = input.into_iter().next().unwrap().span().source_file();

    if !file.is_real() {
        panic!("Must be called from a file.");
    }

    file.path().canonicalize().unwrap()
}

fn strip_manifest_dir(path: PathBuf) -> PathBuf {
    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();

    path.strip_prefix(manifest_dir).unwrap().into()
}

fn strip_file_name(path: PathBuf) -> PathBuf {
    path.parent().unwrap().into()
}

fn replace_file_name(path: PathBuf, file_name: String) -> PathBuf {
    strip_file_name(path).join(file_name)
}