mapper-impl 1.1.2

Implementation detail of the `mapper` crate
Documentation
use std::{collections::HashSet, fmt::Debug};
use syn::{Data, DeriveInput, Error, Path, Result};

use crate::attr::{
    field::params::Params, mapping_strategy::MappingStrategy, spanned_item::SpannedItem, to::To,
};

use self::{
    data_type::Struct,
    mapping_field::MappingField,
    mapping_tree::{MappingTree, MappingType},
};

pub mod data_type;
pub mod field;
pub mod mapping_field;
pub mod mapping_tree;

#[derive(Debug)]
pub enum Input<'a> {
    Struct(Struct<'a>),
}

impl<'a> Input<'a> {
    pub fn from_syn(node: &'a DeriveInput) -> Result<Self> {
        match &node.data {
            Data::Struct(data) => Struct::from_syn(node, data).map(Input::Struct),
            _ => Err(Error::new_spanned(
                node,
                "Only c style structs are supported",
            )),
        }
    }
}

impl From<Struct<'_>> for HashSet<MappingTree> {
    fn from(value: Struct<'_>) -> Self {
        let mut mapping_trees = HashSet::new();
        initialize_automatic_mapping_trees(&mut mapping_trees, &value);
        for field in &value.fields {
            add_all_fields_to_automatic_mapping_trees(&mut mapping_trees, field);
            for field_to in &field.attrs.to.to_items {
                if field_to.params.exclude.1 {
                    remove_excluded_fields_for_mapping_trees(&mut mapping_trees, field_to, field);
                    continue;
                }
                for field_strategy in &field_to.params.strategies {
                    add_mapping_field_for_additive_mapping_trees(
                        &mut mapping_trees,
                        &value,
                        field_to,
                        &field_strategy.1,
                        field,
                    );
                }
                for with in &field_to.params.with {
                    add_with_function(&mut mapping_trees, &value, field_to, with, field);
                }
                add_non_strategy_dependent_fields_params(&mut mapping_trees, field_to, field);
            }
        }
        mapping_trees
    }
}

fn add_with_function(
    mapping_trees: &mut HashSet<MappingTree>,
    value: &Struct,
    field_to: &To<Params>,
    with: &SpannedItem<Path, MappingStrategy>,
    field: &field::Field,
) {
    if field_to.params.destination.is_none() {
        return;
    }
    let field_dest = field_to.params.destination.as_ref().unwrap();
    let mut mapping_tree = mapping_trees
        .get(&MappingTree::new(
            value.ident.clone(),
            field_dest.clone(),
            with.1.clone(),
            None,
        ))
        .unwrap()
        .clone();
    mapping_tree.mapping_fields.replace(MappingField {
        ty: field.ty.clone(),
        member: field.member.clone(),
        field: field_to.params.field.clone(),
        strategy: with.1.clone(),
        with: Option::flatten(
            field_to
                .params
                .with
                .iter()
                .find(|&w| w.1 == with.1)
                .map(|w| w.0.clone()),
        ),
    });
    mapping_trees.replace(mapping_tree);
}

fn add_non_strategy_dependent_fields_params(
    mapping_trees: &mut HashSet<MappingTree>,
    field_to: &To<Params>,
    field: &field::Field,
) {
    if field_to.params.destination.is_none() {
        return;
    }
    let updated_mapping_trees = mapping_trees
        .iter()
        .filter(|&mapping_tree| {
            field_to.params.destination.is_none()
                || &mapping_tree.destination == field_to.params.destination.as_ref().unwrap()
        })
        .map(|mapping_tree| {
            let mut mapping_tree = mapping_tree.clone();
            let updated_fields = mapping_tree
                .mapping_fields
                .iter()
                .filter(|f| f.member == field.member)
                .map(|f| MappingField {
                    field: field_to.params.field.clone(),
                    ..f.clone()
                })
                .collect::<Vec<MappingField>>();
            for mapping_field in updated_fields {
                mapping_tree.mapping_fields.replace(mapping_field);
            }
            mapping_tree
        })
        .collect::<Vec<MappingTree>>();
    for mapping_tree in updated_mapping_trees {
        mapping_trees.replace(mapping_tree);
    }
}

fn add_mapping_field_for_additive_mapping_trees(
    mapping_trees: &mut HashSet<MappingTree>,
    value: &Struct,
    field_to: &To<Params>,
    field_strategy: &MappingStrategy,
    field: &field::Field,
) {
    if field_to.params.destination.is_none() {
        return;
    }
    let field_dest = field_to.params.destination.as_ref().unwrap();
    let mut mapping_tree = mapping_trees
        .get_or_insert(MappingTree::new(
            value.ident.clone(),
            field_dest.clone(),
            field_strategy.clone(),
            Some(MappingType::Additive),
        ))
        .clone();
    mapping_tree.mapping_fields.replace(MappingField {
        ty: field.ty.clone(),
        member: field.member.clone(),
        field: field_to.params.field.clone(),
        strategy: field_strategy.clone(),
        with: Option::flatten(
            field_to
                .params
                .with
                .iter()
                .find(|w| &w.1 == field_strategy)
                .map(|w| w.0.clone()),
        ),
    });
    mapping_trees.replace(mapping_tree);
}

fn remove_excluded_fields_for_mapping_trees(
    mapping_trees: &mut HashSet<MappingTree>,
    field_to: &To<Params>,
    field: &field::Field,
) {
    let mapping_trees_with_excluded_field_removed = mapping_trees
        .iter()
        .filter(|&mapping_tree| {
            field_to.params.destination.is_none()
                || &mapping_tree.destination == field_to.params.destination.as_ref().unwrap()
        })
        .map(|mapping_tree| {
            let mut mapping_tree = mapping_tree.clone();
            mapping_tree.remove_mapping_fields_by_member(&field.member);
            mapping_tree
        })
        .collect::<Vec<MappingTree>>();
    for mapping_tree in mapping_trees_with_excluded_field_removed {
        mapping_trees.replace(mapping_tree);
    }
}

fn add_all_fields_to_automatic_mapping_trees(
    mapping_trees: &mut HashSet<MappingTree>,
    field: &field::Field,
) {
    let automatic_trees = mapping_trees
        .iter()
        .filter(|&mapping_tree| mapping_tree.has_mapping_type(&MappingType::Automatic))
        .map(|mapping_tree| {
            let mut mapping_tree = mapping_tree.clone();
            mapping_tree.mapping_fields.replace(MappingField {
                ty: field.ty.clone(),
                strategy: mapping_tree.strategy.clone(),
                member: field.member.clone(),
                field: None,
                with: None,
            });
            mapping_tree
        })
        .collect::<Vec<MappingTree>>();
    for automatic_tree in automatic_trees {
        mapping_trees.replace(automatic_tree);
    }
}

fn initialize_automatic_mapping_trees(mapping_trees: &mut HashSet<MappingTree>, value: &Struct) {
    let struct_to = &value.attrs.to;
    for (strategy, destinations) in &struct_to.destinations_by_strategy {
        for destination in destinations {
            mapping_trees.insert(MappingTree::new(
                value.ident.clone(),
                destination.clone(),
                strategy.1.clone(),
                Some(MappingType::Automatic),
            ));
        }
    }
}