Skip to content

maxwase/abstract-getters

Repository files navigation

Abstract getters

crates doc

Abstract what, how and from where one wants to get a value using 2 traits!

Forget about get_ref, get_mut, into_inner and others, one get to rule them all!

How it works

Under the hood, the abstract_getters_derive's macro generates 3 implementations of the same trait: for T, &T, and &mut T. It also creates a module with the StructName converted into snake_case. The module contains generated ZST structs with the same name as the struct's fields. You can later use a qualified path like struct_name::foo to get the field. Whether the field is returned as a reference, mutable reference, or an owned value depends on the provided value to the getter; types do not need to be adjusted manually!

Examples

In the example below you can see how the type is determined by the ownership that the get method is given.

use abstract_getters::Get;
use abstract_getters_derive::Getters;

#[derive(Getters)]
struct ConcreteStruct {
    a: i32,
    b: String,
    c: (usize, &'static str),
}

fn main() {
    let mut concrete = ConcreteStruct {
        a: 24,
        b: "Hello".to_string(),
        c: (7, "World"),
    };

    {
        let a/* : &i32 */ = (&concrete).get::<concrete_struct::a>();
        let b/* : &String */ = (&concrete).get::<concrete_struct::b>();
        let c/* : &(usize, &str) */ = (&concrete).get::<concrete_struct::c>();
    }

    {
        let a/* : &mut i32 */ = (&mut concrete).get::<concrete_struct::a>();
        let b/* : &mut String */ = (&mut concrete).get::<concrete_struct::b>();
        let c/* : &mut (usize, &str) */ = (&mut concrete).get::<concrete_struct::c>();
    }
    {
        let a/* : i32 */ = concrete.get::<concrete_struct::a>();
        // The value was moved
        // let b/* : String */ = concrete.clone().get::<concrete_struct::b>();
        // let c/* : (usize, &str) */ = concrete.clone().get::<concrete_struct::c>();
    }
}

It's possible to require one or more fields from a value. It can be useful when designing an interface, which accepts a large struct with a lot of fields, when in reality it would only need a couple. Using these getters, you can design refactor-proof APIs! It's also possible to give users opportunities to define their own types instead of requiring them to implement yet another trait that you would need to maintain. It's always easier to define a module/struct alias rather than have duplicated fields!

use abstract_getters::{Field, Get};
use abstract_getters_derive::Getters;

#[derive(Getters, Default)]
struct LargeStruct {
    // The function need only a
    a: i32,
    // and b
    b: String,

    // To be refactored
    c: f64,
    d: bool,
    // 100500 more fields
}

fn main() {
    let v = LargeStruct::default();

    require_i32_and_string::<large_struct::a, large_struct::b, _>(&v);
}

/// For a `V`alue, that has a Namei32 [Field] with a [type](Field::Type) [i32]...
/// For a `V`alue, that has a NameString [Field] with a [type](Field::Type) [String]...
fn require_i32_and_string<'v, Namei32, NameString, V>(from: &'v V)
where
    &'v V: Get + Field<Namei32, Type = &'v i32> + Field<NameString, Type = &'v String>,
{
    let got_i32 = from.get::<Namei32>();
    assert_eq!(std::any::type_name_of_val(&got_i32), "&i32");

    let got_string = from.get::<NameString>();
    assert_eq!(std::any::type_name_of_val(&got_string), "&alloc::string::String");
}

It is also possible to derive getters for enums, no more as_, try_ and into_!

use abstract_getters::{Get, Field};
use abstract_getters_derive::Getters;

#[derive(Getters)]
enum DataEnum {
    Inline {
        a: i32,
        b: String,
    },
    Tuple(i32, String),
    #[allow(unused)]
    Unit,
    Single(i32),
}

fn main() {
    let mut data = DataEnum::Tuple(42, "Hello".to_string());

    let wrong_variant/* : Option<&i32> */ = (&data).get::<data_enum::inline::a>();
    assert_eq!(wrong_variant, None);
    let int/* : Option<&mut i32> */ = (&mut data).get::<data_enum::tuple::_0>();
    assert_eq!(*int.unwrap(), 42);
}

About

Abstract how, what and from where to get a value using a trait

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages