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
extern crate attr;

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

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

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

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

#[derive(Debug)]
pub struct Top {
    foo: Foo
}


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 Vector;

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

    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) -> Self::Output {
            i.bar.as_ref()
        }

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

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

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

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

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

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

    impl<'a> IterableAttr<'a, &'a Foo> for Vector {
        type Item = &'a Bla;

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


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"
        }
    }
}

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

    use super::Top;
    use super::Foo;

    #[derive(Default)]
    pub struct FooField;

    #[derive(Default)]
    pub struct TopAttributes {
        pub foo: FooField,
    }

    impl Attributes<TopAttributes> for Top {
        fn attrs() -> TopAttributes {
            TopAttributes::default()
        }
    }

    impl<'a> Attr<&'a Top> for FooField {
        type Output = &'a Foo;

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

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

#[test]
fn test_access() {
    let b1 = Bla { name: "foo".into() };
    let b2 = Bla { name: "bla".into() };

    let f = Foo { bar: "bar".into(), vector: vec![b1,b2] };
    let top = Top { foo: f };

    let path = retrieve(foo::Vector).from(top::FooField);

    assert_eq!(path.traverse(&top).unwrap().len(), 2);
}

#[test]
fn test_mapped() {
    let b1 = Bla { name: "foo".into() };
    let b2 = Bla { name: "bla".into() };

    let f = Foo { bar: "bar".into(), vector: vec![b1,b2] };
    let path = retrieve(bla::Name).mapped(foo::Vector);

    let result = path.traverse(&f).unwrap().map(std::result::Result::unwrap).collect::<Vec<_>>();
    assert_eq!(result, vec!["foo", "bla"]);
}

#[test]
fn test_complex_mapped() {
    let b1 = Bla { name: "foo".into() };
    let b2 = Bla { name: "bla".into() };

    let f = Foo { bar: "bar".into(), vector: vec![b1,b2] };
    let top = Top { foo: f };

    let path = retrieve(bla::Name).mapped(foo::Vector).from(top::FooField);

    let result = path.traverse(&top).unwrap().map(std::result::Result::unwrap).collect::<Vec<_>>();
    assert_eq!(result, vec!["foo", "bla"]);
}