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
#[macro_use] extern crate failure;
pub use cardparse_derive::*;

#[derive(Debug,Fail)]
pub enum ParseError {
    #[fail(display = "Field '{}' could not be parsed from '{}' because the source was too short", field, source_line)]
    SourceTooShort{field: String, start: usize, end: Option<usize>, line: usize, source_line: String},
}

/// The `CardParse` trait should be derived for structs as `#[derive(CardParse)]`
/// struct fields are then tagged with attributes like `#[location(line=2,start=6,end=24)]`.
/// **Values to the field attributes are by index not offset (start at one) and
/// always inclusive.**
/// 
/// ```
/// use cardparse::prelude::*;
/// #[derive(CardParse,Debug)]
/// struct FirstNoEnd {
///     #[location(line=1,start=1)]
///     field_one: String,
///     #[location(line=2,start=6,max=24)]
///     field_two: String,
///     #[location(line=2,start=1,end=5)]
///     field_three: String,
/// }
/// ```
/// 
/// CardpParse protects against overlapping attributes at compile time
/// 
/// ```compile_fail
/// use cardparse::prelude::*;
/// #[derive(CardParse,Debug)]
/// struct FirstNoEnd {
///     #[location(line=1,start=1.end=4)]
///     field_one: String,
///     #[location(line=1,start=2,end=6)]
///     field_two: String,
/// }
/// ```

pub trait CardParse {
    fn cardparse(s: &str) -> Result<Self, ParseError> where Self: Sized;
}

pub mod prelude {
    pub use crate::{CardParse, ParseError};
}

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

    #[derive(CardParse,Debug)]
    struct Simple {
        #[location(line=1,start=1,end=12)]
        field_one: String,
        #[location(line=2,start=6,end=12)]
        field_two: String,
    }

    #[derive(CardParse,Debug)]
    struct FirstNoEnd {
        #[location(line=1,start=1)]
        field_one: String,
        #[location(line=2,start=6,max=24)]
        field_two: String,
        #[location(line=2,start=1,end=5)]
        field_three: String,
    }

    #[test]
    fn simple_test() {
        let simple = Simple::cardparse("Some String it is\nAnd also some other string");
        assert!(simple.is_ok());
        let simple = simple.unwrap();
        assert_eq!(simple.field_one, "Some String ");
        assert_eq!(simple.field_two, "lso som");
        
    }

    #[test]
    fn first_no_end_test() {
        let first_no_end = FirstNoEnd::cardparse("Some String it is\nAnd also some other string");
        assert!(first_no_end.is_ok());
        let first_no_end = first_no_end.unwrap();
        assert_eq!(first_no_end.field_one, "Some String it is");
        assert_eq!(first_no_end.field_two, "lso some other stri");
        assert_eq!(first_no_end.field_three, "And a");
    }

     #[test]
    fn first_no_end_failure_test() {
        let first_no_end = FirstNoEnd::cardparse("Some String it is\nAnd");
        assert!(first_no_end.is_err());
        assert_eq!(
            format!("{}", first_no_end.unwrap_err()), 
            "Field 'field_two' could not be parsed from 'Some String it is\nAnd' because the source was too short"
        );
    }
}