blob: 211b646b2a13eb7cfaf8249592a278e223b78299 [file] [log] [blame] [edit]
//! Getters internals
use std::convert::TryFrom;
use proc_macro2::{TokenStream, Span};
use quote::quote;
use syn::{
DeriveInput,
FieldsNamed,
Type,
AttrStyle,
Ident,
LitStr,
Result,
Error,
Attribute,
parse::{Parse, ParseStream},
};
use crate::{
extract::{named_fields, named_struct},
faultmsg::Problem,
};
#[derive(Debug, Clone, PartialEq, Eq)]
enum Action {
Skip,
Rename(Ident),
}
impl Parse for Action {
fn parse(input: ParseStream) -> Result<Self> {
syn::custom_keyword!(skip);
syn::custom_keyword!(rename);
if input.peek(skip) {
let _ = input.parse::<skip>()?;
if !input.is_empty() {
Err(Error::new(Span::call_site(), Problem::TokensFollowSkip))
} else {
Ok(Action::Skip)
}
} else if input.peek(rename) {
let _ = input.parse::<rename>()?;
let _ = input.parse::<syn::Token![=]>()?;
let name = input.parse::<LitStr>()?;
if !input.is_empty() {
Err(Error::new(Span::call_site(), Problem::TokensFollowNewName))
} else {
Ok(Action::Rename(Ident::new(name.value().as_str(), Span::call_site())))
}
} else {
Err(Error::new(Span::call_site(), Problem::InvalidAttribute))
}
}
}
fn get_action_from(attributes: &[Attribute]) -> Result<Option<Action>> {
let mut current: Option<Action> = None;
for attr in attributes {
if attr.style != AttrStyle::Outer { continue; }
if attr.path.is_ident("getter") {
current = Some(attr.parse_args::<Action>()?);
}
}
Ok(current)
}
pub struct Field {
ty: Type,
name: Ident,
getter: Ident,
}
impl Field {
fn from_field(field: &syn::Field) -> Result<Option<Self>> {
let name: Ident = field.ident
.clone()
.ok_or(Error::new(Span::call_site(), Problem::UnnamedField))?;
match get_action_from(field.attrs.as_slice())? {
Some(Action::Skip) => return Ok(None),
Some(Action::Rename(ident)) => Ok(Some(Field {
ty: field.ty.clone(),
name: name,
getter: ident,
})),
None => Ok(Some(Field {
ty: field.ty.clone(),
name: name.clone(),
getter: name,
})),
}
}
fn from_fields_named(fields_named: &FieldsNamed) -> Result<Vec<Self>> {
fields_named.named
.iter()
.try_fold(Vec::new(), |mut fields, field| {
if let Some(field) = Field::from_field(field)? {
fields.push(field);
}
Ok(fields)
})
}
fn emit(&self) -> TokenStream {
let returns = &self.ty;
let field_name = &self.name;
let getter_name = &self.getter;
match &self.ty {
Type::Reference(tr) => {
let lifetime = tr.lifetime.as_ref();
quote!(
pub fn #getter_name(&#lifetime self) -> #returns {
self.#field_name
}
)
},
_ => {
quote!(
pub fn #getter_name(&self) -> &#returns {
&self.#field_name
}
)
},
}
}
}
pub struct NamedStruct<'a> {
original: &'a DeriveInput,
name: Ident,
fields: Vec<Field>,
}
impl<'a> NamedStruct<'a> {
pub fn emit(&self) -> TokenStream {
let (impl_generics, struct_generics, where_clause) = self.original.generics
.split_for_impl();
let struct_name = &self.name;
let methods: Vec<TokenStream> = self.fields
.iter()
.map(|field| field.emit())
.collect();
quote!(
impl #impl_generics #struct_name #struct_generics
#where_clause
{
#(#methods)*
}
)
}
}
impl<'a> TryFrom<&'a DeriveInput> for NamedStruct<'a> {
type Error = Error;
fn try_from(node: &'a DeriveInput) -> Result<Self> {
let struct_data = named_struct(node)?;
let named_fields = named_fields(struct_data)?;
let fields = Field::from_fields_named(named_fields)?;
Ok(NamedStruct {
original: node,
name: node.ident.clone(),
fields,
})
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn parse_action() -> Result<()> {
let a: Action = syn::parse_str("skip")?;
assert!(a == Action::Skip);
let r: Result<Action> = syn::parse_str("skip = blah");
assert!(r.is_err());
let a: Action = syn::parse_str("rename = \"hello\"")?;
let check = Action::Rename(Ident::new("hello", Span::call_site()));
assert!(a == check);
let r: Result<Action> = syn::parse_str("rename + \"chooga\"");
assert!(r.is_err());
let r: Result<Action> = syn::parse_str("rename = \"chooga\" | bongle");
assert!(r.is_err());
Ok(())
}
}