mirror of
https://github.com/mbilker/kbinxml-rs.git
synced 2026-04-24 06:57:15 -05:00
psmap: rustfmt
This commit is contained in:
parent
0660dcda3f
commit
9780b171d1
|
|
@ -1,524 +1,515 @@
|
|||
extern crate proc_macro;
|
||||
|
||||
#[macro_use] extern crate quote;
|
||||
#[macro_use] extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{TokenStreamExt, ToTokens};
|
||||
use syn::{Expr, Ident, LitStr, Token, Type, parse_macro_input};
|
||||
use quote::{ToTokens, TokenStreamExt};
|
||||
use syn::parse::{Parse, ParseStream, Result};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::token::Brace;
|
||||
use syn::{parse_macro_input, Expr, Ident, LitStr, Token, Type};
|
||||
|
||||
mod kw {
|
||||
custom_keyword!(attributes);
|
||||
custom_keyword!(default);
|
||||
custom_keyword!(include);
|
||||
custom_keyword!(inputs);
|
||||
custom_keyword!(output);
|
||||
custom_keyword!(optional);
|
||||
custom_keyword!(transform);
|
||||
custom_keyword!(value);
|
||||
custom_keyword!(attributes);
|
||||
custom_keyword!(default);
|
||||
custom_keyword!(include);
|
||||
custom_keyword!(inputs);
|
||||
custom_keyword!(output);
|
||||
custom_keyword!(optional);
|
||||
custom_keyword!(transform);
|
||||
custom_keyword!(value);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Output {
|
||||
struct_name: Ident,
|
||||
struct_name: Ident,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Includes {
|
||||
includes: Punctuated<Ident, Token![,]>,
|
||||
includes: Punctuated<Ident, Token![,]>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SourceMapping {
|
||||
source: LitStr,
|
||||
target: Ident,
|
||||
target_type: Option<Type>,
|
||||
source: LitStr,
|
||||
target: Ident,
|
||||
target_type: Option<Type>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Mapping {
|
||||
source: LitStr,
|
||||
attributes: Option<Punctuated<SourceMapping, Token![,]>>,
|
||||
subnodes: Option<Punctuated<Mapping, Token![,]>>,
|
||||
value: Option<Ident>,
|
||||
transform: Option<Expr>,
|
||||
default_value: Option<Expr>,
|
||||
optional_value: bool,
|
||||
source: LitStr,
|
||||
attributes: Option<Punctuated<SourceMapping, Token![,]>>,
|
||||
subnodes: Option<Punctuated<Mapping, Token![,]>>,
|
||||
value: Option<Ident>,
|
||||
transform: Option<Expr>,
|
||||
default_value: Option<Expr>,
|
||||
optional_value: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct InputBlock {
|
||||
name: Ident,
|
||||
mappings: Punctuated<Mapping, Token![,]>,
|
||||
name: Ident,
|
||||
mappings: Punctuated<Mapping, Token![,]>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Inputs {
|
||||
blocks: Punctuated<InputBlock, Token![,]>,
|
||||
blocks: Punctuated<InputBlock, Token![,]>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Psmap {
|
||||
output: Output,
|
||||
includes: Option<Includes>,
|
||||
inputs: Inputs,
|
||||
output: Output,
|
||||
includes: Option<Includes>,
|
||||
inputs: Inputs,
|
||||
}
|
||||
|
||||
struct PsmapOutput {
|
||||
struct_name: Ident,
|
||||
definitions: TokenStream2,
|
||||
fields: TokenStream2,
|
||||
struct_name: Ident,
|
||||
definitions: TokenStream2,
|
||||
fields: TokenStream2,
|
||||
}
|
||||
|
||||
impl Parse for Output {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
input.parse::<kw::output>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
input.parse::<kw::output>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
|
||||
let struct_name = input.parse()?;
|
||||
input.parse::<Token![,]>()?;
|
||||
let struct_name = input.parse()?;
|
||||
input.parse::<Token![,]>()?;
|
||||
|
||||
Ok(Self {
|
||||
struct_name,
|
||||
})
|
||||
}
|
||||
Ok(Self { struct_name })
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Includes {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
input.parse::<kw::include>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
input.parse::<kw::include>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
|
||||
let content;
|
||||
let _ = bracketed!(content in input);
|
||||
let mut includes = content.parse_terminated(Ident::parse)?;
|
||||
let content;
|
||||
let _ = bracketed!(content in input);
|
||||
let mut includes = content.parse_terminated(Ident::parse)?;
|
||||
|
||||
if !includes.trailing_punct() {
|
||||
let span = includes.span();
|
||||
includes.push_punct(Token);
|
||||
if !includes.trailing_punct() {
|
||||
let span = includes.span();
|
||||
includes.push_punct(Token);
|
||||
}
|
||||
|
||||
input.parse::<Token![,]>()?;
|
||||
|
||||
Ok(Self { includes })
|
||||
}
|
||||
|
||||
input.parse::<Token![,]>()?;
|
||||
|
||||
Ok(Self {
|
||||
includes,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Includes {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
self.includes.to_tokens(tokens);
|
||||
}
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
self.includes.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for SourceMapping {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let source: LitStr = input.parse()?;
|
||||
input.parse::<Token![=>]>()?;
|
||||
let target: Ident = input.parse()?;
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let source: LitStr = input.parse()?;
|
||||
input.parse::<Token![=>]>()?;
|
||||
let target: Ident = input.parse()?;
|
||||
|
||||
let lookahead = input.lookahead1();
|
||||
let target_type = if lookahead.peek(Token![as]) {
|
||||
input.parse::<Token![as]>()?;
|
||||
let lookahead = input.lookahead1();
|
||||
let target_type = if lookahead.peek(Token![as]) {
|
||||
input.parse::<Token![as]>()?;
|
||||
|
||||
let target_type: Type = input.parse()?;
|
||||
let target_type: Type = input.parse()?;
|
||||
|
||||
Some(target_type)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some(target_type)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
source,
|
||||
target,
|
||||
target_type,
|
||||
})
|
||||
}
|
||||
Ok(Self {
|
||||
source,
|
||||
target,
|
||||
target_type,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Mapping {
|
||||
fn sub_node_parse(source: LitStr, input: ParseStream) -> Result<Self> {
|
||||
let content;
|
||||
let _ = braced!(content in input);
|
||||
fn sub_node_parse(source: LitStr, input: ParseStream) -> Result<Self> {
|
||||
let content;
|
||||
let _ = braced!(content in input);
|
||||
|
||||
let attributes = if content.parse::<Option<kw::attributes>>()?.is_some() {
|
||||
//eprintln!("Mapping: attributes");
|
||||
content.parse::<Token![:]>()?;
|
||||
let attributes = if content.parse::<Option<kw::attributes>>()?.is_some() {
|
||||
//eprintln!("Mapping: attributes");
|
||||
content.parse::<Token![:]>()?;
|
||||
|
||||
let attr_content;
|
||||
let _ = braced!(attr_content in content);
|
||||
let attributes = attr_content.parse_terminated(SourceMapping::parse)?;
|
||||
let attr_content;
|
||||
let _ = braced!(attr_content in content);
|
||||
let attributes = attr_content.parse_terminated(SourceMapping::parse)?;
|
||||
|
||||
content.parse::<Token![,]>()?;
|
||||
Some(attributes)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
content.parse::<Token![,]>()?;
|
||||
Some(attributes)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let value = if content.parse::<Option<kw::value>>()?.is_some() {
|
||||
//eprintln!("Mapping: value");
|
||||
content.parse::<Token![=>]>()?;
|
||||
let value = if content.parse::<Option<kw::value>>()?.is_some() {
|
||||
//eprintln!("Mapping: value");
|
||||
content.parse::<Token![=>]>()?;
|
||||
|
||||
let value = content.parse()?;
|
||||
content.parse::<Token![,]>()?;
|
||||
let value = content.parse()?;
|
||||
content.parse::<Token![,]>()?;
|
||||
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let transform = if content.parse::<Option<kw::transform>>()?.is_some() {
|
||||
//eprintln!("Mapping: transform");
|
||||
content.parse::<Token![=>]>()?;
|
||||
let transform = if content.parse::<Option<kw::transform>>()?.is_some() {
|
||||
//eprintln!("Mapping: transform");
|
||||
content.parse::<Token![=>]>()?;
|
||||
|
||||
let value = content.parse()?;
|
||||
content.parse::<Token![,]>()?;
|
||||
let value = content.parse()?;
|
||||
content.parse::<Token![,]>()?;
|
||||
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let default_value = if content.parse::<Option<kw::default>>()?.is_some() {
|
||||
//eprintln!("Mapping: default");
|
||||
content.parse::<Token![=>]>()?;
|
||||
let default_value = if content.parse::<Option<kw::default>>()?.is_some() {
|
||||
//eprintln!("Mapping: default");
|
||||
content.parse::<Token![=>]>()?;
|
||||
|
||||
let value = content.parse()?;
|
||||
content.parse::<Token![,]>()?;
|
||||
let value = content.parse()?;
|
||||
content.parse::<Token![,]>()?;
|
||||
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let optional_value = if content.parse::<Option<kw::optional>>()?.is_some() {
|
||||
//eprintln!("Mapping: optional");
|
||||
content.parse::<Token![,]>()?;
|
||||
let optional_value = if content.parse::<Option<kw::optional>>()?.is_some() {
|
||||
//eprintln!("Mapping: optional");
|
||||
content.parse::<Token![,]>()?;
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let subnodes = content.parse_terminated(Mapping::parse)?;
|
||||
let subnodes = content.parse_terminated(Mapping::parse)?;
|
||||
|
||||
Ok(Self {
|
||||
source,
|
||||
attributes,
|
||||
subnodes: Some(subnodes),
|
||||
value,
|
||||
transform,
|
||||
default_value,
|
||||
optional_value,
|
||||
})
|
||||
}
|
||||
Ok(Self {
|
||||
source,
|
||||
attributes,
|
||||
subnodes: Some(subnodes),
|
||||
value,
|
||||
transform,
|
||||
default_value,
|
||||
optional_value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Mapping {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let source: LitStr = input.parse()?;
|
||||
input.parse::<Token![=>]>()?;
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let source: LitStr = input.parse()?;
|
||||
input.parse::<Token![=>]>()?;
|
||||
|
||||
let lookahead = input.lookahead1();
|
||||
if lookahead.peek(Brace) {
|
||||
Self::sub_node_parse(source, input)
|
||||
} else if lookahead.peek(Ident) {
|
||||
Ok(Self {
|
||||
source,
|
||||
attributes: None,
|
||||
subnodes: None,
|
||||
value: input.parse()?,
|
||||
transform: None,
|
||||
default_value: None,
|
||||
optional_value: false,
|
||||
})
|
||||
} else {
|
||||
panic!("unknown mapping type found");
|
||||
let lookahead = input.lookahead1();
|
||||
if lookahead.peek(Brace) {
|
||||
Self::sub_node_parse(source, input)
|
||||
} else if lookahead.peek(Ident) {
|
||||
Ok(Self {
|
||||
source,
|
||||
attributes: None,
|
||||
subnodes: None,
|
||||
value: input.parse()?,
|
||||
transform: None,
|
||||
default_value: None,
|
||||
optional_value: false,
|
||||
})
|
||||
} else {
|
||||
panic!("unknown mapping type found");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for InputBlock {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let name = input.parse()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let name = input.parse()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
|
||||
let content;
|
||||
let _ = braced!(content in input);
|
||||
let mappings = content.parse_terminated(Mapping::parse)?;
|
||||
let content;
|
||||
let _ = braced!(content in input);
|
||||
let mappings = content.parse_terminated(Mapping::parse)?;
|
||||
|
||||
Ok(Self {
|
||||
name,
|
||||
mappings,
|
||||
})
|
||||
}
|
||||
Ok(Self { name, mappings })
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Inputs {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
input.parse::<kw::inputs>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
input.parse::<kw::inputs>()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
|
||||
let content;
|
||||
let _ = bracketed!(content in input);
|
||||
let blocks = content.parse_terminated(InputBlock::parse)?;
|
||||
let content;
|
||||
let _ = bracketed!(content in input);
|
||||
let blocks = content.parse_terminated(InputBlock::parse)?;
|
||||
|
||||
input.parse::<Option<Token![,]>>()?;
|
||||
input.parse::<Option<Token![,]>>()?;
|
||||
|
||||
Ok(Self {
|
||||
blocks,
|
||||
})
|
||||
}
|
||||
Ok(Self { blocks })
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Psmap {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let mut output: Option<Output> = None;
|
||||
let mut includes: Option<Includes> = None;
|
||||
let mut inputs: Option<Inputs> = None;
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let mut output: Option<Output> = None;
|
||||
let mut includes: Option<Includes> = None;
|
||||
let mut inputs: Option<Inputs> = None;
|
||||
|
||||
loop {
|
||||
let lookahead = input.lookahead1();
|
||||
if lookahead.peek(kw::output) {
|
||||
output = Some(input.parse()?);
|
||||
} else if lookahead.peek(kw::include) {
|
||||
includes = Some(input.parse()?);
|
||||
} else if lookahead.peek(kw::inputs) {
|
||||
inputs = Some(input.parse()?);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
loop {
|
||||
let lookahead = input.lookahead1();
|
||||
if lookahead.peek(kw::output) {
|
||||
output = Some(input.parse()?);
|
||||
} else if lookahead.peek(kw::include) {
|
||||
includes = Some(input.parse()?);
|
||||
} else if lookahead.peek(kw::inputs) {
|
||||
inputs = Some(input.parse()?);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let output = output.unwrap();
|
||||
let inputs = inputs.unwrap();
|
||||
|
||||
//eprintln!("struct_name: {}", output.struct_name);
|
||||
|
||||
/*
|
||||
if let Some(ref includes) = includes {
|
||||
for include in includes.includes.iter() {
|
||||
eprintln!("include: {:?}", include);
|
||||
}
|
||||
}
|
||||
|
||||
let remaining: TokenStream2 = input.parse()?;
|
||||
eprintln!("remaining: {}", remaining);
|
||||
*/
|
||||
|
||||
Ok(Self {
|
||||
output,
|
||||
includes,
|
||||
inputs,
|
||||
})
|
||||
}
|
||||
|
||||
let output = output.unwrap();
|
||||
let inputs = inputs.unwrap();
|
||||
|
||||
//eprintln!("struct_name: {}", output.struct_name);
|
||||
|
||||
/*
|
||||
if let Some(ref includes) = includes {
|
||||
for include in includes.includes.iter() {
|
||||
eprintln!("include: {:?}", include);
|
||||
}
|
||||
}
|
||||
|
||||
let remaining: TokenStream2 = input.parse()?;
|
||||
eprintln!("remaining: {}", remaining);
|
||||
*/
|
||||
|
||||
Ok(Self {
|
||||
output,
|
||||
includes,
|
||||
inputs,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PsmapOutput {
|
||||
fn value_for_pair(
|
||||
&mut self,
|
||||
mapping: &Mapping,
|
||||
target: &Ident,
|
||||
) -> TokenStream2 {
|
||||
let struct_name = &self.struct_name;
|
||||
let Mapping {
|
||||
source,
|
||||
transform,
|
||||
default_value,
|
||||
optional_value,
|
||||
..
|
||||
} = mapping;
|
||||
fn value_for_pair(&mut self, mapping: &Mapping, target: &Ident) -> TokenStream2 {
|
||||
let struct_name = &self.struct_name;
|
||||
let Mapping {
|
||||
source,
|
||||
transform,
|
||||
default_value,
|
||||
optional_value,
|
||||
..
|
||||
} = mapping;
|
||||
|
||||
let transform = transform.as_ref().map(|transform| {
|
||||
quote_spanned! {transform.span()=>
|
||||
let child_value = #transform(child_value)?;
|
||||
}
|
||||
});
|
||||
let map_value = match default_value {
|
||||
Some(default_value) => quote_spanned! {source.span()=>
|
||||
.unwrap_or_else(#default_value)
|
||||
},
|
||||
None => quote_spanned! {source.span()=>
|
||||
.ok_or(::psmap::PsmapError::ValueNotFound {
|
||||
source_name: stringify!(#source),
|
||||
})?
|
||||
},
|
||||
};
|
||||
|
||||
let definition_tokens = quote_spanned! {target.span()=>
|
||||
let mut #target = None;
|
||||
};
|
||||
// This part is a little more confusing, but here's the process.
|
||||
//
|
||||
// `Node::value` returns `Option<&Value>` and `TryInto::try_into` should only be called
|
||||
// if there is `Some(value)`, but this returns `Option<Result<T, E>>`. `Option::transpose`
|
||||
// converts that to `Result<Option<T>, E>` which `?` can be used on.
|
||||
let body_tokens = quote_spanned! {source.span()=>
|
||||
let child_value = child.value()
|
||||
.map(|v| v.try_into())
|
||||
.transpose()?
|
||||
#map_value;
|
||||
#transform
|
||||
#target = Some(child_value);
|
||||
};
|
||||
let field_tokens = match default_value {
|
||||
Some(default_value) => quote_spanned! {target.span()=>
|
||||
#target: #target.unwrap_or_else(#default_value),
|
||||
},
|
||||
None if *optional_value => quote_spanned! {target.span()=>
|
||||
#target,
|
||||
},
|
||||
None => quote_spanned! {target.span()=>
|
||||
#target: #target.ok_or(::psmap::PsmapError::FieldNotFound {
|
||||
target: stringify!(#target),
|
||||
struct_name: stringify!(#struct_name),
|
||||
})?,
|
||||
},
|
||||
};
|
||||
|
||||
self.definitions.append_all(definition_tokens);
|
||||
self.fields.append_all(field_tokens);
|
||||
|
||||
body_tokens
|
||||
}
|
||||
|
||||
fn handle_mapping(&mut self, mapping: &Mapping) -> TokenStream2 {
|
||||
let Mapping {
|
||||
source,
|
||||
attributes,
|
||||
subnodes,
|
||||
value,
|
||||
..
|
||||
} = mapping;
|
||||
|
||||
let mut body = TokenStream2::new();
|
||||
|
||||
//eprintln!("source: {}, value: {:?}", source.value(), value);
|
||||
|
||||
if let Some(value_target) = value {
|
||||
let body_tokens = self.value_for_pair(mapping, value_target);
|
||||
body.append_all(body_tokens);
|
||||
}
|
||||
|
||||
if let Some(attributes) = attributes {
|
||||
let struct_name = &self.struct_name;
|
||||
|
||||
for SourceMapping { source: attr, target, target_type } in attributes.iter() {
|
||||
let target_type = target_type.as_ref().map(|target_type| {
|
||||
quote! {
|
||||
::<#target_type>
|
||||
}
|
||||
let transform = transform.as_ref().map(|transform| {
|
||||
quote_spanned! {transform.span()=>
|
||||
let child_value = #transform(child_value)?;
|
||||
}
|
||||
});
|
||||
|
||||
self.definitions.append_all(quote_spanned! {attr.span()=>
|
||||
let mut #target = None;
|
||||
});
|
||||
body.append_all(quote_spanned! {attr.span()=>
|
||||
#target = Some(child.attr(#attr).ok_or(::psmap::PsmapError::AttributeNotFound {
|
||||
attribute: #attr,
|
||||
source_name: #source,
|
||||
struct_name: stringify!(#struct_name),
|
||||
})?
|
||||
.parse#target_type()
|
||||
.map_err(|source| ::psmap::PsmapError::AttributeParse {
|
||||
attribute: #attr,
|
||||
source_name: #source,
|
||||
struct_name: stringify!(#struct_name),
|
||||
source: Box::new(source),
|
||||
})?);
|
||||
});
|
||||
self.fields.append_all(quote_spanned! {target.span()=>
|
||||
#target: #target.ok_or(::psmap::PsmapError::FieldNotFoundFromSource {
|
||||
target: stringify!(#target),
|
||||
source_name: #source,
|
||||
struct_name: stringify!(#struct_name),
|
||||
})?,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let inner_loop: Option<TokenStream2> = if let Some(subnodes) = subnodes {
|
||||
let input = Ident::new("child", source.span());
|
||||
Some(self.create_input_loop(&input, subnodes.iter()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
quote_spanned! {source.span()=>
|
||||
#source => {
|
||||
#body
|
||||
#inner_loop
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn create_input_loop<'a, I>(&mut self, input: &Ident, mappings: I) -> TokenStream2
|
||||
where I: Iterator<Item = &'a Mapping>
|
||||
{
|
||||
let mut mapping_tokens = TokenStream2::new();
|
||||
|
||||
for mapping in mappings {
|
||||
let matching_arm = self.handle_mapping(mapping);
|
||||
mapping_tokens.append_all(matching_arm);
|
||||
}
|
||||
|
||||
quote! {
|
||||
for child in #input.children_iter() {
|
||||
match child.key() {
|
||||
#mapping_tokens
|
||||
_ => {},
|
||||
let map_value = match default_value {
|
||||
Some(default_value) => quote_spanned! {source.span()=>
|
||||
.unwrap_or_else(#default_value)
|
||||
},
|
||||
None => quote_spanned! {source.span()=>
|
||||
.ok_or(::psmap::PsmapError::ValueNotFound {
|
||||
source_name: stringify!(#source),
|
||||
})?
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
let definition_tokens = quote_spanned! {target.span()=>
|
||||
let mut #target = None;
|
||||
};
|
||||
// This part is a little more confusing, but here's the process.
|
||||
//
|
||||
// `Node::value` returns `Option<&Value>` and `TryInto::try_into` should only be called
|
||||
// if there is `Some(value)`, but this returns `Option<Result<T, E>>`. `Option::transpose`
|
||||
// converts that to `Result<Option<T>, E>` which `?` can be used on.
|
||||
let body_tokens = quote_spanned! {source.span()=>
|
||||
let child_value = child.value()
|
||||
.map(|v| v.try_into())
|
||||
.transpose()?
|
||||
#map_value;
|
||||
#transform
|
||||
#target = Some(child_value);
|
||||
};
|
||||
let field_tokens = match default_value {
|
||||
Some(default_value) => quote_spanned! {target.span()=>
|
||||
#target: #target.unwrap_or_else(#default_value),
|
||||
},
|
||||
None if *optional_value => quote_spanned! {target.span()=>
|
||||
#target,
|
||||
},
|
||||
None => quote_spanned! {target.span()=>
|
||||
#target: #target.ok_or(::psmap::PsmapError::FieldNotFound {
|
||||
target: stringify!(#target),
|
||||
struct_name: stringify!(#struct_name),
|
||||
})?,
|
||||
},
|
||||
};
|
||||
|
||||
self.definitions.append_all(definition_tokens);
|
||||
self.fields.append_all(field_tokens);
|
||||
|
||||
body_tokens
|
||||
}
|
||||
|
||||
fn handle_mapping(&mut self, mapping: &Mapping) -> TokenStream2 {
|
||||
let Mapping {
|
||||
source,
|
||||
attributes,
|
||||
subnodes,
|
||||
value,
|
||||
..
|
||||
} = mapping;
|
||||
|
||||
let mut body = TokenStream2::new();
|
||||
|
||||
//eprintln!("source: {}, value: {:?}", source.value(), value);
|
||||
|
||||
if let Some(value_target) = value {
|
||||
let body_tokens = self.value_for_pair(mapping, value_target);
|
||||
body.append_all(body_tokens);
|
||||
}
|
||||
|
||||
if let Some(attributes) = attributes {
|
||||
let struct_name = &self.struct_name;
|
||||
|
||||
for SourceMapping {
|
||||
source: attr,
|
||||
target,
|
||||
target_type,
|
||||
} in attributes.iter()
|
||||
{
|
||||
let target_type = target_type.as_ref().map(|target_type| {
|
||||
quote! {
|
||||
::<#target_type>
|
||||
}
|
||||
});
|
||||
|
||||
self.definitions.append_all(quote_spanned! {attr.span()=>
|
||||
let mut #target = None;
|
||||
});
|
||||
body.append_all(quote_spanned! {attr.span()=>
|
||||
#target = Some(child.attr(#attr).ok_or(::psmap::PsmapError::AttributeNotFound {
|
||||
attribute: #attr,
|
||||
source_name: #source,
|
||||
struct_name: stringify!(#struct_name),
|
||||
})?
|
||||
.parse#target_type()
|
||||
.map_err(|source| ::psmap::PsmapError::AttributeParse {
|
||||
attribute: #attr,
|
||||
source_name: #source,
|
||||
struct_name: stringify!(#struct_name),
|
||||
source: Box::new(source),
|
||||
})?);
|
||||
});
|
||||
self.fields.append_all(quote_spanned! {target.span()=>
|
||||
#target: #target.ok_or(::psmap::PsmapError::FieldNotFoundFromSource {
|
||||
target: stringify!(#target),
|
||||
source_name: #source,
|
||||
struct_name: stringify!(#struct_name),
|
||||
})?,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let inner_loop: Option<TokenStream2> = if let Some(subnodes) = subnodes {
|
||||
let input = Ident::new("child", source.span());
|
||||
Some(self.create_input_loop(&input, subnodes.iter()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
quote_spanned! {source.span()=>
|
||||
#source => {
|
||||
#body
|
||||
#inner_loop
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn create_input_loop<'a, I>(&mut self, input: &Ident, mappings: I) -> TokenStream2
|
||||
where
|
||||
I: Iterator<Item = &'a Mapping>,
|
||||
{
|
||||
let mut mapping_tokens = TokenStream2::new();
|
||||
|
||||
for mapping in mappings {
|
||||
let matching_arm = self.handle_mapping(mapping);
|
||||
mapping_tokens.append_all(matching_arm);
|
||||
}
|
||||
|
||||
quote! {
|
||||
for child in #input.children_iter() {
|
||||
match child.key() {
|
||||
#mapping_tokens
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn psmap(input: TokenStream) -> TokenStream {
|
||||
let Psmap {
|
||||
output: Output {
|
||||
struct_name,
|
||||
},
|
||||
includes,
|
||||
inputs: Inputs {
|
||||
blocks,
|
||||
},
|
||||
} = parse_macro_input!(input as Psmap);
|
||||
let Psmap {
|
||||
output: Output { struct_name },
|
||||
includes,
|
||||
inputs: Inputs { blocks },
|
||||
} = parse_macro_input!(input as Psmap);
|
||||
|
||||
let mut output = PsmapOutput {
|
||||
struct_name: struct_name.clone(),
|
||||
definitions: TokenStream2::new(),
|
||||
fields: TokenStream2::new(),
|
||||
};
|
||||
let mut output = PsmapOutput {
|
||||
struct_name: struct_name.clone(),
|
||||
definitions: TokenStream2::new(),
|
||||
fields: TokenStream2::new(),
|
||||
};
|
||||
|
||||
let mut loops = TokenStream2::new();
|
||||
for InputBlock { name, mappings } in blocks.iter() {
|
||||
loops.append_all(output.create_input_loop(&name, mappings.iter()));
|
||||
}
|
||||
|
||||
let definitions = output.definitions;
|
||||
let fields = output.fields;
|
||||
|
||||
let output = quote! {
|
||||
{
|
||||
use std::convert::TryInto;
|
||||
|
||||
#definitions
|
||||
#loops
|
||||
|
||||
#struct_name {
|
||||
#includes
|
||||
#fields
|
||||
}
|
||||
let mut loops = TokenStream2::new();
|
||||
for InputBlock { name, mappings } in blocks.iter() {
|
||||
loops.append_all(output.create_input_loop(&name, mappings.iter()));
|
||||
}
|
||||
};
|
||||
//eprintln!("output: {}", output);
|
||||
|
||||
output.into()
|
||||
let definitions = output.definitions;
|
||||
let fields = output.fields;
|
||||
|
||||
let output = quote! {
|
||||
{
|
||||
use std::convert::TryInto;
|
||||
|
||||
#definitions
|
||||
#loops
|
||||
|
||||
#struct_name {
|
||||
#includes
|
||||
#fields
|
||||
}
|
||||
}
|
||||
};
|
||||
//eprintln!("output: {}", output);
|
||||
|
||||
output.into()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user