ppx_macros/
lib.rs

1#![cfg_attr(feature = "nightly", feature(proc_macro_tracked_path))]
2
3use std::path::PathBuf;
4
5use proc_macro::Span;
6use syn::parse::Parse;
7use syn::{Expr, ExprArray, LitStr, Token};
8use quote::ToTokens;
9
10use ppx_impl as ppx;
11
12struct Args {
13    file_path: String,
14    base_path: String,
15    params: Vec<String>,
16}
17
18impl Parse for Args {
19    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
20        let file_path: LitStr = input.parse()?;
21        input.parse::<Token![,]>()?;
22
23        let mut params: Option<ExprArray> = None;
24        let base_path: LitStr = input.parse()?;
25        if input.peek(Token![,]) {
26            input.parse::<Token![,]>()?;
27            if !input.is_empty() {
28                params = Some(input.parse()?);
29            }
30        }
31
32        let params = params
33            .map(|params| {
34                params.elems
35                    .into_iter()
36                    .map(|elem| match elem {
37                        Expr::Lit(lit) => match &lit.lit {
38                            syn::Lit::Str(s) => s.value(),
39                            _ => panic!("Expected string literal in params"),
40                        },
41                        _ => panic!("Expected string literal in params")
42                    }).collect::<Vec<_>>()
43            })
44            .unwrap_or(Vec::new());
45
46        Ok(Args {
47            file_path: file_path.value(),
48            base_path: base_path.value(),
49            params: params
50        })
51    }
52}
53
54/// Parse a macro at compile time from a file.
55///
56/// # Example
57///
58/// ```no_run
59/// include_ppx_string!("path/to/file", "./templates", ["param1", "param2"])
60/// ```
61#[cfg(feature = "nightly")]
62#[proc_macro]
63pub fn include_ppx(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
64    let args = syn::parse_macro_input!(input as Args);
65
66    let source_path = PathBuf::from(Span::call_site().file());
67    let base_path = source_path.parent().unwrap();
68
69    let file_path = base_path.join(args.file_path);
70    proc_macro::tracked::path(file_path.to_str().expect("File path was not UTF-8 encoded"));
71    let base_path = base_path.join(args.base_path);
72
73    let output = ppx::parse(file_path, base_path, args.params.iter().map(|s| s.as_str())).unwrap();
74    let output = LitStr::new(&output, Span::call_site().into());
75
76    return output.to_token_stream().into();
77}
78
79/// Parse a macro at compile time from a string.
80///
81/// # Example
82///
83/// ```rust
84/// # use ppx_macros::include_ppx_string;
85/// assert_eq!(
86///     include_ppx_string!("#define A Hello\nA", ".", []),
87///     "Hello"
88/// );
89/// ```
90#[proc_macro]
91pub fn include_ppx_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
92    let args = syn::parse_macro_input!(input as Args);
93
94    let source_path = PathBuf::from(Span::call_site().file());
95    let base_path = source_path.parent().unwrap();
96
97    let contents = args.file_path;
98    let base_path = base_path.join(args.base_path);
99
100    let output = ppx::parse_string(&contents, base_path, args.params.iter().map(|s| s.as_str())).unwrap();
101    let output = LitStr::new(&output, Span::call_site().into());
102
103    return output.to_token_stream().into();
104}