attr 0.1.0

`attr` is a library to provide external access to a datastructure through a typed path object, using all type information known about the data structure at hand.
Documentation
#![feature(test)]

extern crate attr;
extern crate test;

use test::{Bencher, black_box};

use attr::retrieve;
use attr::Traverse;

trait Attributes<AttributeType> {
    fn attrs() -> AttributeType;
}

#[derive(Debug)]
pub struct Foo {
    bar: String,
    batz: Bla,
    numbers: Vec<i32>
}

#[derive(Debug)]
pub struct Bla {
    name: String
}

pub mod foo {
    use attr::Attr;
    use attr::IndexableAttr;
    use attr::IterableAttr;
    use super::Attributes;

    use super::Foo;
    use super::Bla;

    #[derive(Default)]
    pub struct Bar;
    #[derive(Default)]
    pub struct Batz;
    #[derive(Default)]
    pub struct Numbers;

    #[derive(Default)]
    pub struct FooAttributes {
        pub bar: Bar,
        pub batz: Batz,
        pub numbers: Numbers
    }

    impl Attributes<FooAttributes> for Foo {
        fn attrs() -> FooAttributes {
            FooAttributes::default()
        }
    }

    impl<'a> Attr<&'a Foo> for Bar {
        type Output = &'a str;

        fn get(&self, i: &'a Foo) -> &'a str {
            i.bar.as_ref()
        }

        fn name(&self) -> &'static str {
            "bar"
        }
    }

    impl<'a> Attr<&'a mut Foo> for Bar {
        type Output = &'a mut String;

        fn get(&self, i: &'a mut Foo) -> &'a mut String {
            &mut i.bar
        }

        fn name(&self) -> &'static str {
            "bar"
        }
    }

    impl<'a> Attr<&'a Foo> for Batz {
        type Output = &'a Bla;

        fn get(&self, i: &'a Foo) -> &'a Bla {
            &i.batz
        }

        fn name(&self) -> &'static str {
            "batz"
        }
    }

    impl<'a> Attr<&'a mut Foo> for Batz {
        type Output = &'a mut Bla;

        fn get(&self, i: &'a mut Foo) -> &'a mut Bla {
            &mut i.batz
        }

        fn name(&self) -> &'static str {
            "batz"
        }
    }

    impl<'a> Attr<&'a Foo> for Numbers {
        type Output = &'a[i32];

        fn get(&self, i: &'a Foo) -> &'a[i32] {
            i.numbers.as_ref()
        }

        fn name(&self) -> &'static str {
            "numbers"
        }
    }

    impl<'a> Attr<&'a mut Foo> for Numbers {
        type Output = &'a mut Vec<i32>;

        fn get(&self, i: &'a mut Foo) -> &'a mut Vec<i32> {
            &mut i.numbers
        }

        fn name(&self) -> &'static str {
            "numbers"
        }
    }

    impl<'a> IndexableAttr<&'a Foo, usize> for Numbers {
        type Output = i32;

        fn at(&self, i: &'a Foo, idx: usize) -> i32 {
            self.get(i)[idx]
        }
    }

    impl<'a> IndexableAttr<&'a mut Foo, usize> for Numbers {
        type Output = &'a mut i32;

        fn at(&self, i: &'a mut Foo, idx: usize) -> &'a mut i32 {
            unsafe { self.get(i).get_unchecked_mut(idx) }
        }
    }

    impl<'a> IterableAttr<'a, &'a Foo> for Numbers {
        type Item = &'a i32;

        fn iter(&self, i: &'a Foo) -> Box<Iterator<Item=&'a i32> + 'a> {
            Box::new(self.get(i).iter())
        }
    }

    impl<'a> IterableAttr<'a, &'a mut Foo> for Numbers {
        type Item = &'a mut i32;

        fn iter(&self, i: &'a mut Foo) -> Box<Iterator<Item=&'a mut i32> +'a> {
            Box::new(self.get(i).iter_mut())
        }
    }
}

pub mod bla {
    use attr::Attr;
    use super::Attributes;

    use super::Bla;

    #[derive(Default)]
    pub struct Name;

    #[derive(Default)]
    pub struct BlaAttributes {
        pub name: Name,
    }

    impl Attributes<BlaAttributes> for Bla {
        fn attrs() -> BlaAttributes {
            BlaAttributes::default()
        }
    }

    impl<'a> Attr<&'a Bla> for Name {
        type Output = &'a str;

        fn get(&self, i: &'a Bla) -> &'a str {
            i.name.as_ref()
        }

        fn name(&self) -> &'static str {
            "name"
        }
    }

    impl<'a> Attr<&'a mut Bla> for Name {
        type Output = &'a mut String;

        fn get(&self, i: &'a mut Bla) -> &'a mut String {
            &mut i.name
        }

        fn name(&self) -> &'static str {
            "name"
        }
    }
}

#[bench]
fn direct_access_bench(b: &mut Bencher) {
    let f = Foo { bar: "foobar".into(), batz: Bla { name: "foo".into() }, numbers: vec![1,2,3] };
    b.iter(|| black_box(direct_access(&f)) );
}

#[inline]
fn direct_access(f: &Foo) -> &str {
    &f.batz.name
}

#[bench]
fn through_path(b: &mut Bencher) {
    let f = Foo { bar: "foobar".into(), batz: Bla { name: "foo".into() }, numbers: vec![1,2,3] };
    b.iter(|| black_box(path_access(&f)) );
}

#[inline]
fn path_access(f: &Foo) -> Result<&str, String> {
    let p = retrieve(bla::Name).from(foo::Batz);
    p.traverse(f)
}