extern crate cargo_toml;
extern crate proc_macro;
extern crate proc_macro2;
extern crate quote;
extern crate syn;
extern crate toml;
use crate::{
cargo_toml::Manifest,
proc_macro::TokenStream,
proc_macro2::{Literal, Span as Span2, TokenStream as TokenStream2},
quote::{quote, ToTokens},
syn::{
parse::{Parse, ParseBuffer},
parse_macro_input,
token::Dot,
Error as SynError, Lit, LitBool,
},
toml::Value,
};
enum Index {
Int(usize),
Str(String),
}
struct TomlIndex(Vec<Index>);
impl Parse for TomlIndex {
fn parse(input: &ParseBuffer) -> Result<Self, SynError> {
let mut another_one = true;
let mut index = Vec::new();
while another_one {
index.push(match input.parse::<Lit>() {
Ok(lit) => match lit {
Lit::Str(lit_str) => Index::Str(lit_str.value()),
Lit::Int(lit_int) => Index::Int(
lit_int
.base10_digits()
.parse()
.expect("Cannot parse literal integer"),
),
_ => return Err(SynError::new(input.span(), "Unsupported literal")),
},
Err(e) => {
return Err(SynError::new(
input.span(),
format!("Cannot parse index item: {}", e),
))
}
});
if let Err(_) = input.parse::<Dot>() {
another_one = false;
}
}
Ok(Self(index))
}
}
fn toml_to_ts(input: Value) -> TokenStream2 {
match input {
Value::String(s) => Lit::new(Literal::string(&s)).to_token_stream().into(),
Value::Integer(i) => Lit::new(Literal::i64_suffixed(i)).to_token_stream().into(),
Value::Float(f) => Lit::new(Literal::f64_suffixed(f)).to_token_stream().into(),
Value::Datetime(d) => Lit::new(Literal::string(&d.to_string()))
.to_token_stream()
.into(),
Value::Boolean(b) => Lit::Bool(LitBool::new(b, Span2::call_site()))
.to_token_stream()
.into(),
Value::Array(a) => {
let mut ts = TokenStream2::new();
for value in a {
let v = toml_to_ts(value);
ts.extend(quote! (#v,));
}
quote! ((#ts))
}
Value::Table(t) => {
let mut ts = TokenStream2::new();
for (key, value) in t {
let v = toml_to_ts(value);
ts.extend(quote! ((#key, #v)));
}
quote! ((#ts))
}
}
}
#[proc_macro]
pub fn include_toml(input: TokenStream) -> TokenStream {
let input: TomlIndex = parse_macro_input!(input);
let cargo_toml: Manifest =
Manifest::from_path_with_metadata("Cargo.toml").expect("Cannot read Cargo.toml");
let mut cargo_toml_toml: Value =
Value::try_from(cargo_toml).expect("Cannot parse Cargo.toml to json");
for item in input.0 {
match item {
Index::Int(index) => {
cargo_toml_toml = cargo_toml_toml[index].clone();
}
Index::Str(index) => {
cargo_toml_toml = cargo_toml_toml[index].clone();
}
}
}
toml_to_ts(cargo_toml_toml).into()
}