peer-pressure 0.1.1

Stupidly simple validation library
Documentation
use peer_pressure::Validate;

#[derive(Debug, PartialEq)]
struct RawScore(i32);
#[derive(Debug, PartialEq)]
struct Score(u32);

impl Validate for RawScore {
    type Context = ();
    type Output = Score;
    type Error = &'static str;

    fn validate_in_context(self, _: &()) -> Result<Score, Self::Error> {
        u32::try_from(self.0)
            .map(Score)
            .map_err(|_| "negative score")
    }
}

#[derive(Debug, PartialEq)]
struct RawLabel(&'static str);
#[derive(Debug, PartialEq)]
struct Label(&'static str);

impl Validate for RawLabel {
    type Context = ();
    type Output = Label;
    type Error = &'static str;

    fn validate_in_context(self, _: &()) -> Result<Label, Self::Error> {
        if self.0.is_empty() {
            Err("empty label")
        } else {
            Ok(Label(self.0))
        }
    }
}

#[test]
fn pair_validates_into_a_pair_of_outputs() {
    assert_eq!(
        (RawScore(10), RawLabel("hp")).validate(),
        Ok((Score(10), Label("hp")))
    );
}

#[test]
fn pair_propagates_the_first_element_error() {
    assert_eq!(
        (RawScore(-1), RawLabel("hp")).validate(),
        Err("negative score")
    );
}

#[test]
fn pair_propagates_a_later_element_error() {
    assert_eq!((RawScore(10), RawLabel("")).validate(), Err("empty label"));
}

#[test]
fn pair_short_circuits_before_validating_later_elements() {
    assert_eq!(
        (RawScore(-1), RawLabel("")).validate(),
        Err("negative score")
    );
}

#[test]
fn triple_preserves_element_order() {
    assert_eq!(
        (RawScore(1), RawLabel("a"), RawScore(2)).validate(),
        Ok((Score(1), Label("a"), Score(2)))
    );
}

#[test]
fn nested_tuples_validate_recursively() {
    assert_eq!(
        (RawScore(1), (RawLabel("a"), RawScore(2))).validate(),
        Ok((Score(1), (Label("a"), Score(2))))
    );
}

#[derive(Debug, PartialEq)]
struct Floor(i32);

#[derive(Debug, PartialEq)]
struct RawAbove(i32);
#[derive(Debug, PartialEq)]
struct Above(i32);

impl Validate for RawAbove {
    type Context = Floor;
    type Output = Above;
    type Error = &'static str;

    fn validate_in_context(self, floor: &Floor) -> Result<Above, Self::Error> {
        if self.0 > floor.0 {
            Ok(Above(self.0))
        } else {
            Err("below floor")
        }
    }
}

#[test]
fn tuple_threads_one_shared_context_into_every_element() {
    assert_eq!(
        (RawAbove(5), RawAbove(9)).validate_in_context(&Floor(3)),
        Ok((Above(5), Above(9)))
    );
    assert_eq!(
        (RawAbove(5), RawAbove(1)).validate_in_context(&Floor(3)),
        Err("below floor")
    );
}

#[test]
fn large_tuples_are_supported_up_to_the_documented_ceiling() {
    let raws = (
        RawScore(1),
        RawScore(2),
        RawScore(3),
        RawScore(4),
        RawScore(5),
        RawScore(6),
        RawScore(7),
        RawScore(8),
        RawScore(9),
        RawScore(10),
        RawScore(11),
        RawScore(12),
        RawScore(13),
        RawScore(14),
        RawScore(15),
        RawScore(16),
        RawScore(17),
        RawScore(18),
        RawScore(19),
        RawScore(20),
        RawScore(21),
        RawScore(22),
        RawScore(23),
        RawScore(24),
        RawScore(25),
        RawScore(26),
    );
    let validated = raws.validate().unwrap();
    assert_eq!(validated.0, Score(1));
    assert_eq!(validated.25, Score(26));
}