use anyhow::Result;
use askama::Template;
use proc_macro::{Ident, TokenStream, TokenTree};
use std::collections::VecDeque;
#[derive(Template, Debug)]
#[template(path = "builder.j2", escape = "none")]
pub struct BuilderContext {
name: String, meta_name: String, fields: Vec<Fd>, metas: Vec<Meta>, }
#[derive(Debug, Default)]
struct Fd {
name: String, ty: String, attrs: Vec<FdAttr>,
#[allow(dead_code)]
optional: bool, }
#[derive(Debug)]
struct FdAttr {
name: String,
items: Vec<Item>,
}
#[derive(Debug)]
struct Meta {
name: String,
items: Vec<Item>,
}
#[derive(Debug)]
struct Item {
key: String,
val: String,
}
impl Item {
pub fn new(tokens: Vec<&[TokenTree]>) -> Self {
if tokens.len() == 1 {
match tokens[0].last() {
Some(TokenTree::Ident(name)) => Self {
key: name.to_string(),
val: "".to_string(),
},
e => panic!("Expect ident, got {:?}", e),
}
} else {
let ident = match tokens[0].first() {
Some(TokenTree::Ident(name)) => Some(name),
e => panic!("Expect ident, got {:?}", e),
};
let lit = match tokens[1].last() {
Some(TokenTree::Literal(lit)) => Some(lit.to_string().replace("\"", "")),
e => panic!("Expect ident, got {:?}", e),
};
Self {
key: ident.unwrap().to_string(),
val: lit.unwrap(),
}
}
}
}
impl Fd {
pub fn new(name: &[TokenTree], ty: &[TokenTree]) -> Self {
let ty = ty
.iter()
.map(|v| match v {
TokenTree::Ident(n) => n.to_string(),
TokenTree::Punct(p) => p.as_char().to_string(),
e => panic!("Expect ident, got {:?}", e),
})
.collect::<Vec<_>>();
let mut group_arr = vec![];
for v in name.into_iter() {
if let TokenTree::Group(g) = v.clone() {
group_arr.push(g.stream());
}
}
let attrs = get_field_attr(group_arr);
let fd = match name.last() {
Some(TokenTree::Ident(name)) => {
let (ty, optional) = if ty[0].as_str() == "Option" {
(&ty[2..ty.len() - 1], true)
} else {
(&ty[..], false)
};
Self {
name: name.to_string(),
ty: ty.join(""), attrs,
optional,
}
}
e => panic!("Expect ident, got {:?}", e),
};
fd
}
}
impl BuilderContext {
fn new(input: TokenStream) -> Self {
let metas = get_attr_meta(split_metas(input.clone()));
let (name, input) = split(input);
let fields = get_struct_fields(input);
Self {
meta_name: format!("{}Sql", name),
name: name.to_string(),
fields,
metas,
}
}
pub fn render(input: TokenStream) -> Result<String> {
let template = Self::new(input);
Ok(template.render()?)
}
}
fn split(input: TokenStream) -> (Ident, TokenStream) {
let mut input = input.into_iter().collect::<VecDeque<_>>();
while let Some(item) = input.pop_front() {
if let TokenTree::Ident(v) = item {
if v.to_string() == "struct" {
break;
}
}
}
let ident;
if let Some(TokenTree::Ident(v)) = input.pop_front() {
ident = v;
} else {
panic!("Didn't find struct name");
}
let mut group = None;
for item in input {
if let TokenTree::Group(g) = item {
group = Some(g);
break;
}
}
(ident, group.expect("Didn't find field group").stream())
}
fn split_metas(input: TokenStream) -> Vec<TokenStream> {
let mut input = input.into_iter().collect::<VecDeque<_>>();
while let Some(item) = input.pop_back() {
if let TokenTree::Ident(v) = item {
if v.to_string() == "struct" {
break;
}
}
}
let mut v = vec![];
for item in input {
if let TokenTree::Group(g) = item {
v.push(g.stream());
}
}
v
}
fn get_attr_meta(inputs: Vec<TokenStream>) -> Vec<Meta> {
let mut metas = vec![];
for input in inputs {
let mut input = input.into_iter().collect::<VecDeque<_>>();
let mut ident = None;
let mut items = vec![];
while let Some(item) = input.pop_front() {
if let TokenTree::Ident(v) = item.clone() {
ident = Some(v);
}
if let TokenTree::Group(g) = item {
let input = g.stream().into_iter().collect::<Vec<_>>();
items = input
.split(|v| match v {
TokenTree::Punct(p) => p.as_char() == ',',
_ => false,
})
.map(|tokens| {
tokens
.split(|v| match v {
TokenTree::Punct(p) => p.as_char() == '=',
_ => false,
})
.collect::<Vec<_>>()
})
.map(|tokens| Item::new(tokens))
.collect();
}
}
metas.push(Meta {
name: ident.expect("meta didn't have name").to_string(),
items,
});
}
metas
}
fn get_struct_fields(input: TokenStream) -> Vec<Fd> {
let input = input.into_iter().collect::<VecDeque<_>>();
let input = input.into_iter().collect::<Vec<_>>();
input
.split(|v| {
match v {
TokenTree::Punct(p) => p.as_char() == ',',
_ => false,
}
})
.map(|tokens| {
let re = tokens
.split(|v| match v {
TokenTree::Punct(p) => p.as_char() == ':',
_ => false,
})
.collect::<Vec<_>>();
re
})
.filter(|tokens| tokens.len() == 2)
.map(|tokens| Fd::new(tokens[0], &tokens[1]))
.collect()
}
fn get_field_attr(inputs: Vec<TokenStream>) -> Vec<FdAttr> {
let mut metas = vec![];
for input in inputs {
let mut input = input.into_iter().collect::<VecDeque<_>>();
let mut ident = None;
let mut items = vec![];
while let Some(item) = input.pop_front() {
if let TokenTree::Ident(v) = item.clone() {
ident = Some(v);
}
if let TokenTree::Group(g) = item {
let input = g.stream().into_iter().collect::<Vec<_>>();
items = input
.split(|v| match v {
TokenTree::Punct(p) => p.as_char() == ',',
_ => false,
})
.map(|tokens| {
tokens
.split(|v| match v {
TokenTree::Punct(p) => p.as_char() == '=',
_ => false,
})
.collect::<Vec<_>>()
})
.map(|tokens| Item::new(tokens))
.collect();
}
}
metas.push(FdAttr {
name: ident.expect("meta didn't have name").to_string(),
items,
});
}
metas
}