fixed_width_derive 0.6.0

Derive trait for use with the fixed_width crate.
Documentation
use std::{collections::HashMap, ops::Range};
use syn::LitStr;

pub struct Container {
    pub fixed_width_fn: Option<syn::Ident>,
}

impl Container {
    pub fn from_ast(ast: &syn::DeriveInput) -> Self {
        let mut fixed_width_fn: Option<syn::Ident> = None;

        for attr in &ast.attrs {
            if attr.path().is_ident("fixed_width") {
                attr.parse_nested_meta(|meta| {
                    if meta.path.is_ident("field_def") {
                        let value = meta.value().expect("expected to find an expression, ie fixed_width(field_def = function_name)");
                        let fixed_width_fn_name: LitStr = value.parse().expect("expected to find a function name, ie fixed_width(field_def = function_name)");

                        if fixed_width_fn.is_some() {
                            panic!("expected only 1 function to be specified for the field_def");
                        } else {
                            fixed_width_fn = Some(syn::Ident::new(&fixed_width_fn_name.value(), proc_macro2::Span::call_site()));
                        }
                    }
                    Ok(())
                }).expect("expected fixed_width(...)");
            }
        }

        Self { fixed_width_fn }
    }
}

#[allow(dead_code)]
#[derive(Debug)]
pub struct FieldDef {
    pub ident: syn::Ident,
    pub field_type: syn::Type,
    pub name: String,
    pub pad_with: char,
    pub range: Range<usize>,
    pub justify: String,
}

pub struct Context {
    pub field: syn::Field,
    pub skip: bool,
    pub metadata: HashMap<String, Metadata>,
}

impl Context {
    pub fn from_field(field: &syn::Field) -> Self {
        let mut fixed_width_attr_seen = 0;
        let mut metadata = HashMap::new();
        let mut skip = false;

        for attr in &field.attrs {
            if attr.path().is_ident("fixed_width") {
                fixed_width_attr_seen += 1;
                if fixed_width_attr_seen > 1 {
                    panic!(
                        "Field: {} has more than 1 fixed_width attribute",
                        field.ident.clone().unwrap(),
                    );
                }

                let parse_result = attr.parse_nested_meta(|meta| {
                    let ident = meta.path.get_ident().unwrap().clone();
                    let s: LitStr = meta
                        .value()
                        .expect(
                            "expected to find an expression, ie fixed_width(<field> = <metadata>)",
                        )
                        .parse()
                        .expect("fixed width values must be strings");

                    let mdata = Metadata {
                        name: ident.clone().to_string(),
                        value: s.value().to_string(),
                    };
                    metadata.insert(ident.clone().to_string(), mdata);
                    Ok(())
                });

                if parse_result.is_err() {
                    panic!(
                        "could not parse fixed_width metadata for field: {}",
                        field.ident.clone().unwrap()
                    );
                }
            } else if attr.path().is_ident("serde") {
                let parse_result = attr.parse_nested_meta(|meta| {
                    if meta.path.is_ident("skip") {
                        skip = true;
                    }
                    Ok(())
                });

                if parse_result.is_err() {
                    panic!(
                        "could not parse serde metadata for field: {}",
                        field.ident.clone().unwrap()
                    );
                }
            }
        }

        Self {
            field: field.clone(),
            skip,
            metadata,
        }
    }

    pub fn field_name(&self) -> String {
        self.field.ident.clone().unwrap().to_string()
    }
}

#[allow(dead_code)]
pub struct Metadata {
    pub name: String,
    pub value: String,
}