datasources 0.1.5

Async datasource interfaces, middleware, registry, and test doubles
Documentation
mod change_casing_mapper;
mod dictionary_mapper;
mod flat_to_nested_mapper;
mod i_flat_to_nested_mapper;
mod identity_mapper;
mod mapped_value;
mod pluralizer_mapper;
mod row_type_mapper;
mod transform_converter_mapper;
mod transform_mapper;
mod type_map;
mod type_map_field;
mod type_map_group;

pub use change_casing_mapper::ChangeCasingMapper;
pub use dictionary_mapper::DictionaryMapper;
pub use flat_to_nested_mapper::FlatToNestedMapper;
pub use i_flat_to_nested_mapper::FlatToNestedMapper as FlatToNestedMapperTrait;
pub use identity_mapper::IdentityMapper;
pub use mapped_value::{MappedRow, MappedValue};
pub use pluralizer_mapper::PluralizerMapper;
pub use row_type_mapper::RowTypeMapper;
pub use transform_converter_mapper::TransformConverterMapper;
pub use transform_mapper::TransformMapper;
pub use type_map::TypeMap;
pub use type_map_field::TypeMapField;
pub use type_map_group::TypeMapGroup;

pub trait Mapper<TFrom, TTo> {
    fn map(&self, value: TFrom) -> TTo;
    fn reverse_map(&self, value: TTo) -> TFrom;
}

impl<TFrom, TTo> Mapper<TFrom, TTo> for Box<dyn Mapper<TFrom, TTo> + Send + Sync> {
    fn map(&self, value: TFrom) -> TTo {
        (**self).map(value)
    }

    fn reverse_map(&self, value: TTo) -> TFrom {
        (**self).reverse_map(value)
    }
}

impl<TFrom, TTo> Mapper<TFrom, TTo> for std::sync::Arc<dyn Mapper<TFrom, TTo> + Send + Sync> {
    fn map(&self, value: TFrom) -> TTo {
        (**self).map(value)
    }

    fn reverse_map(&self, value: TTo) -> TFrom {
        (**self).reverse_map(value)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[derive(Clone, Debug, PartialEq, Eq)]
    struct User {
        id: i32,
        name: String,
    }

    #[derive(Clone, Debug, PartialEq, Eq)]
    struct Contacts {
        email: String,
        phone: String,
    }

    #[derive(Clone, Debug, PartialEq, Eq)]
    struct Nested {
        user: User,
        contacts: Contacts,
    }

    #[derive(Clone, Debug, PartialEq, Eq)]
    struct Flat {
        user_id: i32,
        user_name: String,
        contact_email: String,
        contact_phone: String,
    }

    #[derive(Clone, Debug, PartialEq, Eq)]
    struct Contact {
        name: String,
        email: String,
    }

    #[derive(Clone, Debug, PartialEq, Eq)]
    struct ContactRow {
        contact_name: String,
        contact_email: String,
    }

    #[test]
    fn flattens_and_unflattens_user_and_contacts() {
        let nested = Nested {
            user: User {
                id: 1,
                name: "Ada".into(),
            },
            contacts: Contacts {
                email: "ada@x.dev".into(),
                phone: "555".into(),
            },
        };
        let mapper = TransformMapper::new(
            |n: Nested| Flat {
                user_id: n.user.id,
                user_name: n.user.name,
                contact_email: n.contacts.email,
                contact_phone: n.contacts.phone,
            },
            |f: Flat| Nested {
                user: User {
                    id: f.user_id,
                    name: f.user_name,
                },
                contacts: Contacts {
                    email: f.contact_email,
                    phone: f.contact_phone,
                },
            },
        );
        let flat = Mapper::map(&mapper, nested.clone());
        assert_eq!(flat.user_id, 1);
        assert_eq!(flat.user_name, "Ada");
        assert_eq!(flat.contact_email, "ada@x.dev");
        assert_eq!(flat.contact_phone, "555");
        assert_eq!(Mapper::reverse_map(&mapper, flat), nested);
    }

    #[test]
    fn prefixes_a_contact_field() {
        let mapper = TransformMapper::new(
            |name: String| format!("contact_{name}"),
            |name: String| name["contact_".len()..].to_string(),
        );
        let mapped = Mapper::map(&mapper, "email".to_string());
        let reversed = Mapper::reverse_map(&mapper, mapped.clone());
        assert_eq!(mapped, "contact_email");
        assert_eq!(reversed, "email");
    }

    #[test]
    fn maps_a_contact_name_and_email() {
        let contact = Contact {
            name: "Ada".into(),
            email: "ada@x.dev".into(),
        };
        let mapper = TransformMapper::new(
            |item: Contact| ContactRow {
                contact_name: item.name,
                contact_email: item.email,
            },
            |row: ContactRow| Contact {
                name: row.contact_name,
                email: row.contact_email,
            },
        );
        let mapped = Mapper::map(&mapper, contact.clone());
        let reversed = Mapper::reverse_map(&mapper, mapped.clone());
        assert_eq!(
            mapped,
            ContactRow {
                contact_name: "Ada".into(),
                contact_email: "ada@x.dev".into(),
            }
        );
        assert_eq!(reversed, contact);
    }
}