use std::{cmp, ops::Range};
use atoi::atoi;
use card::{cell::Cell, ges::GesType};
#[derive(Debug, PartialEq)]
pub enum Line {
Cells(&'static [Cell]),
Ges(GesType),
Provides(&'static [Cell], Conditional),
Optional(&'static [Cell], u8),
Repeat(&'static [Cell], u8),
Block(&'static [Line], &'static [u8]),
OptionalBlock(&'static [u8], &'static [u8]),
}
#[derive(Debug, PartialEq)]
pub enum Conditional {
RelChar(u8, u8),
Int(Range<u8>, u8),
Number(Range<u8>),
}
#[derive(Debug, PartialEq)]
pub enum CondResult {
Bool(bool),
Number(Option<usize>),
}
impl Conditional {
pub fn evaluate(&self, line: &[u8]) -> CondResult {
use self::CondResult::*;
match *self {
Conditional::RelChar(idx, c) => {
let idx = idx as usize;
Bool(line.get(idx) == Some(&c))
}
Conditional::Int(ref r, b) => {
let range = r.start as usize..cmp::min(line.len(), r.end as usize);
let cell = match line.get(range) {
Some(c) => c,
None => return Bool(false),
};
let firstdigit = cell
.iter()
.position(|b| *b >= b'0' && *b <= b'9')
.unwrap_or(0usize);
Bool(
cell
.get(firstdigit..)
.map(|s| atoi::<usize>(s) == Some(b as usize))
.unwrap_or(false),
)
}
Conditional::Number(ref r) => {
let range = r.start as usize..cmp::min(line.len(), r.end as usize);
let cell = match line.get(range) {
Some(c) => c,
None => return Bool(false),
};
let firstdigit = cell
.iter()
.position(|b| *b >= b'0' && *b <= b'9')
.unwrap_or(0usize);
Number(
cell
.get(firstdigit..)
.map(|s| atoi::<usize>(s))
.unwrap_or(None),
)
}
}
}
}
#[cfg(test)]
mod tests {
use card::line::{CondResult::*, Conditional};
#[test]
fn relchar_can_be_evaluated() {
let cond1 = Conditional::RelChar(2, b'b');
let cond2 = Conditional::RelChar(3, b'b');
let line = "abbxy oaslkj";
assert_eq!(Bool(true), cond1.evaluate(line.as_ref()));
assert_eq!(Bool(false), cond2.evaluate(line.as_ref()));
}
#[test]
fn relchar_out_of_bounds() {
let cond1 = Conditional::RelChar(95, b'b');
let line = "abbxy oaslkj";
assert_eq!(Bool(false), cond1.evaluate(line.as_ref()));
}
}