dto_derive 0.1.1

Derive for automatic mapping DTOs to Entities and vice versa.
Documentation
use proc_macro2::Span;
use syn::parse::{Parse, ParseStream};
use syn::{Error, Expr, Ident, Lit, Path, Result, Token};

use super::SpannedParse;
use crate::mapping::{Mapping, MappingSource, MappingTarget};

#[derive(Debug)]
pub(crate) struct MapStructAttr {
    pub(crate) mapping: Mapping,
    pub(crate) span: Span,
}

impl SpannedParse for MapStructAttr {
    fn parse(input: ParseStream, span: Span) -> Result<Self> {
        input.parse::<super::kw::map>()?;
        input.parse::<Token![=]>()?;
        let map_lit = input.parse::<Lit>()?;
        if let Lit::Str(ref map_expr) = map_lit {
            struct MapStructAttrLit(Ident, Expr);
            impl Parse for MapStructAttrLit {
                fn parse(input: ParseStream) -> Result<Self> {
                    let left = input.parse::<Ident>()?;
                    input.parse::<Token![:]>()?;
                    let right = input.parse::<Expr>()?;
                    Ok(MapStructAttrLit(left, right))
                }
            }

            let MapStructAttrLit(left, right) = map_expr.parse()?;
            match right {
                Expr::Path(ref expr) if expr.path.check_ident() => Ok(MapStructAttr {
                    mapping: Mapping {
                        target: MappingTarget(left),
                        source: MappingSource::Field(expr.path.into_ident()),
                    },
                    span,
                }),
                _ => Err(Error::new_spanned(right, "unexpected expression")),
            }
        } else {
            Err(Error::new_spanned(
                map_lit,
                "expected string literal containing mapping expression",
            ))
        }
    }
}

trait IntoIdent {
    fn check_ident(&self) -> bool;
    fn into_ident(&self) -> Ident;
}

impl IntoIdent for Path {
    fn check_ident(&self) -> bool {
        self.leading_colon.is_none()
            && self.segments.len() == 1
            && self.segments[0].arguments.is_empty()
            && self.segments[0]
                .ident
                .to_string()
                .chars()
                .next()
                .unwrap()
                .is_lowercase()
    }

    fn into_ident(&self) -> Ident {
        self.segments[0].ident.clone()
    }
}