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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/// A Trait used by `scanf` to obtain the Regex of a Type
///
/// Has one associated Constant: `REGEX`, which should be set to a regular Expression.
/// Implement this trait for a Type that you want to be parsed using scanf.
///
/// The Regular Expression should match the string representation as exactly as possible.
/// Any incorrect matches might be caught in the from_str parsing, but that might cause this
/// regex to take characters that could have been matched by other placeholders, leading to
/// unexpected parsing failures. Also: Since `scanf` only returns an `Option` it will just say
/// `None` whether the regex matching failed or the parsing failed, so you should avoid parsing
/// failures by writing a proper regex as much as possible.
///
/// ## Example
/// Let's say we want to add a Fraction parser
/// ```
/// # #[derive(Debug, PartialEq)]
/// struct Fraction(isize, usize);
/// ```
/// Which can be obtained from any string of the kind `±X/Y` or just `X`
/// ```
/// # #[derive(Debug, PartialEq)]
/// # struct Fraction(isize, usize);
/// impl sscanf::RegexRepresentation for Fraction {
///     /// matches an optional '-' or '+' followed by a number.
///     /// possibly with a '/' and another Number
///     const REGEX: &'static str = r"[-+]?\d+(/\d+)?";
/// }
/// impl std::str::FromStr for Fraction {
///     type Err = std::num::ParseIntError;
///     fn from_str(s: &str) -> Result<Self, Self::Err> {
///         let mut iter = s.split('/');
///         let num = iter.next().unwrap().parse::<isize>()?;
///         let mut denom = 1;
///         if let Some(d) = iter.next() {
///             denom = d.parse::<usize>()?;
///         }
///         Ok(Fraction(num, denom))
///     }
/// }
/// ```
/// Now we can use this `Fraction` struct in `scanf`:
/// ```
/// # #[derive(Debug, PartialEq)]
/// # struct Fraction(isize, usize);
/// # impl sscanf::RegexRepresentation for Fraction {
/// #     const REGEX: &'static str = r"[-+]?\d+(/\d+)?";
/// # }
/// # impl std::str::FromStr for Fraction {
/// #     type Err = std::num::ParseIntError;
/// #     fn from_str(s: &str) -> Result<Self, Self::Err> {
/// #         let mut iter = s.split('/');
/// #         let num = iter.next().unwrap().parse::<isize>()?;
/// #         let mut denom = 1;
/// #         if let Some(d) = iter.next() {
/// #             denom = d.parse::<usize>()?;
/// #         }
/// #         Ok(Fraction(num, denom))
/// #     }
/// # }
/// use sscanf::scanf;
///
/// let output = scanf!("2/5", "{}", Fraction);
/// assert_eq!(output, Some(Fraction(2, 5)));
///
/// let output = scanf!("-25/3", "{}", Fraction);
/// assert_eq!(output, Some(Fraction(-25, 3)));
///
/// let output = scanf!("8", "{}", Fraction);
/// assert_eq!(output, Some(Fraction(8, 1)));
///
/// let output = scanf!("6e/3", "{}", Fraction);
/// assert_eq!(output, None);
///
/// let output = scanf!("6/-3", "{}", Fraction);
/// assert_eq!(output, None); // only first number can be negative
///
/// let output = scanf!("6/3", "{}", Fraction);
/// assert_eq!(output, Some(Fraction(6, 3)));
/// ```
pub trait RegexRepresentation {
    /// A regular Expression that exactly matches any String representation of the implementing Type
    const REGEX: &'static str;
}

macro_rules! impl_num {
    (u64: $($ty: ty),+) => {
        $(impl RegexRepresentation for $ty {
            /// Matches any positive number
            ///
            /// The length of this match might not fit into the size of the type
            const REGEX: &'static str = r"\+?\d+";
        })+
    };
    (i64: $($ty: ty),+) => {
        $(impl RegexRepresentation for $ty {
            /// Matches any positive or negative number
            ///
            /// The length of this match might not fit into the size of the type
            const REGEX: &'static str = r"[-+]?\d+";
        })+
    };
    (f64: $($ty: ty),+) => {
        $(impl RegexRepresentation for $ty {
            /// Matches any floating point number
            ///
            /// Does **NOT** support stuff like `inf` `nan` or `3e10`. See [`FullF32`](crate::FullF32) for those.
            const REGEX: &'static str = r"[-+]?\d+\.?\d*";
        })+
    };
}

impl_num!(u64: usize, u64, u128);
impl_num!(i64: isize, i64, i128);
impl_num!(f64: f32, f64);

impl RegexRepresentation for String {
    /// Matches any sequence of Characters
    const REGEX: &'static str = r".+";
}
impl RegexRepresentation for char {
    /// Matches a single Character
    const REGEX: &'static str = r".";
}
impl RegexRepresentation for bool {
    /// Matches `true` or `false`
    const REGEX: &'static str = r"true|false";
}

impl RegexRepresentation for u8 {
    /// Matches a number with up to 3 digits.
    ///
    /// The Number matched by this might be too big for the type
    const REGEX: &'static str = r"\+?\d{1,3}";
}
impl RegexRepresentation for u16 {
    /// Matches a number with up to 5 digits.
    ///
    /// The Number matched by this might be too big for the type
    const REGEX: &'static str = r"\+?\d{1,5}";
}
impl RegexRepresentation for u32 {
    /// Matches a number with up to 10 digits.
    ///
    /// The Number matched by this might be too big for the type
    const REGEX: &'static str = r"\+?\d{1,10}";
}
impl RegexRepresentation for i8 {
    /// Matches a number with possible sign and up to 3 digits.
    ///
    /// The Number matched by this might be too big for the type
    const REGEX: &'static str = r"[-+]?\d{1,3}";
}
impl RegexRepresentation for i16 {
    /// Matches a number with possible sign and up to 5 digits.
    ///
    /// The Number matched by this might be too big for the type
    const REGEX: &'static str = r"[-+]?\d{1,5}";
}
impl RegexRepresentation for i32 {
    /// Matches a number with possible sign and up to 10 digits.
    ///
    /// The Number matched by this might be too big for the type
    const REGEX: &'static str = r"[-+]?\d{1,10}";
}