1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/*!
Traits and structures for selecting a field from a data structure.
*/
use std::marker::PhantomData;
use field::Value;
use field::{FieldIdent};
use access::{DataIndex};
use error::*;
use data_types::{AssocTypes, DataType, TypeSelector, DTypeList};

/// Type for accessing a specified field (identified by a `FieldIdent`) for an underlying data
/// structure.
#[derive(Debug, Clone)]
pub struct Selection<DTypes, D, T> {
    /// Underlying data structure for this selection. Contains the field identified by `ident`.
    data: D,
    /// Identifier of the field within the `data` structure.
    pub(crate) ident: FieldIdent,
    _marker_dt: PhantomData<DTypes>,
    _marker_t: PhantomData<T>
}
impl<DTypes, D, T> Selection<DTypes, D, T> {
    /// Create a new `Selection` object from specified data and identifier.
    pub fn new(data: D, ident: FieldIdent) -> Selection<DTypes, D, T> {
        Selection {
            data,
            ident,
            _marker_dt: PhantomData,
            _marker_t: PhantomData,
        }
    }
}
impl<DTypes, U, T> DataIndex<DTypes> for Selection<DTypes, U, T>
    where DTypes: DTypeList,
          T: DataType<DTypes>,
          U: DataIndex<DTypes, DType=T>,
{
    type DType = T;

    fn get_datum(&self, idx: usize) -> Result<Value<&T>> {
        self.data.get_datum(idx)
    }
    fn len(&self) -> usize {
        self.data.len()
    }
}

/// Trait for accessing the data of a single field as a [Selection](struct.Selection.html) struct
/// which implements [DataIndex](../access/trait.DataIndex.html).
pub trait Field<DTypes>
    where DTypes: DTypeList
{
    /// Returns a [Selection](struct.Selection.html) struct containing the data for the field
    /// specified by `ident`.
    ///
    /// This method is a convenience method for calling the [select](trait.SelectField.html#select)
    /// method on the [SelectField](trait.SelectField.html) trait.
    fn field<'a, T: 'a + DataType<DTypes>, I: Into<FieldIdent>>(&'a self, ident: I)
        -> Result<Selection<DTypes, <Self as SelectField<'a, T, DTypes>>::Output, T>>
        where Self: SelectField<'a, T, DTypes>,
              DTypes: 'a + AssocTypes,
              DTypes::Storage: TypeSelector<DTypes, T>
    {
        let ident = ident.into();
        SelectField::select(self, ident.clone())
            .map(|data| Selection::new(data, ident))
    }
}

/// Trait implemented by data structures to provide access to data for a single field.
pub trait SelectField<'a, T, DTypes>
    where DTypes: DTypeList,
          T: 'a + DataType<DTypes>
{
    /// The return type for the `select` method.
    type Output: DataIndex<DTypes, DType=T>;

    /// Returns an object that provides [DataIndex](../access/trait.DataIndex.html) access to the
    /// data in the field specified by `ident`.
    fn select(&'a self, ident: FieldIdent) -> Result<Self::Output>
        where DTypes: AssocTypes,
              DTypes::Storage: TypeSelector<DTypes, T>;
}

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

    use field::Value;
    use test_utils::*;
    use access::DataIndex;
    use error::*;

    #[test]
    fn select() {
        let dv = sample_merged_emp_table();
        println!("{}", dv);
        let result = dv.field("EmpId").unwrap().iter()
            .map(|datum: Value<&u64>| if datum.exists() { 1i64 } else { 0 })
            .collect::<Vec<_>>();
        assert_eq!(result, vec![1, 1, 1, 1, 1, 1, 1]);
    }

    #[test]
    fn select_wrong_type() {
        let dv = sample_merged_emp_table();
        println!("{}", dv);
        let result = dv.field::<i64, _>("EmpId");
        match result {
            Err(AgnesError::IncompatibleTypes { .. }) => {},
            Err(_) => { panic!["wrong error when calling field() with incorrect type"]; },
            Ok(_) => { panic!["expected error when calling field() with incorrect type, but \
                               received Ok"]; }
        }
    }
}