multi_try 0.3.0

Safely combine results
Documentation
use multi_try::MultiTry;

#[derive(Debug, PartialEq)]
struct Email<'a> {
    to: &'a str,
    from: &'a str,
    subject: &'a str,
    body: &'a str,
}

#[derive(Debug, PartialEq)]
struct ValidatedEmail<'a> {
    to: &'a str,
    from: &'a str,
    subject: &'a str,
    body: &'a str,
}

#[derive(Debug, PartialEq)]
enum EmailValidationErr {
    InvalidEmailAddress,
    InvalidRecipientEmailAddress,
    InvalidSenderEmailAddress,
    InvalidSubject,
    InvalidBody,
}

fn validate_address(address: &str) -> Result<&str, EmailValidationErr> {
    if address.contains("@") {
        Ok(address)
    } else {
        Err(EmailValidationErr::InvalidEmailAddress)
    }
}

fn validate_subject(subject: &str) -> Result<&str, EmailValidationErr> {
    if subject.len() > 5 {
        Ok(subject)
    } else {
        Err(EmailValidationErr::InvalidSubject)
    }
}

fn validate_body(body: &str) -> Result<&str, EmailValidationErr> {
    if body.len() > 10 {
        Ok(body)
    } else {
        Err(EmailValidationErr::InvalidBody)
    }
}

fn validate_email(email: Email) -> Result<ValidatedEmail, Vec<EmailValidationErr>> {
    let (to, from, subject, body) = validate_address(email.to).map_err(|_| {
        EmailValidationErr::InvalidRecipientEmailAddress
    }).and_try(validate_address(email.from).map_err(|_| {
        EmailValidationErr::InvalidSenderEmailAddress
    })).and_try(
        validate_subject(email.subject)
    ).and_try(
        validate_body(email.body)
    )?;

    Ok(ValidatedEmail { to, from, subject, body })
}

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

    #[test]
    fn all_err() {
        let a = Email {
            to: "Tom",
            from: "Mary",
            subject: "s",
            body: "b",
        };

        let result = validate_email(a);
        let expected = Err(vec![
            EmailValidationErr::InvalidRecipientEmailAddress,
            EmailValidationErr::InvalidSenderEmailAddress,
            EmailValidationErr::InvalidSubject,
            EmailValidationErr::InvalidBody,
        ]);

        assert_eq!(expected, result);
    }

    #[test]
    fn body_err() {
        let a = Email {
            to: "Tom@mail.com",
            from: "Mary@mail.com",
            subject: "good morning",
            body: "b",
        };

        let result = validate_email(a);
        let expected = Err(vec![
            EmailValidationErr::InvalidBody,
        ]);

        assert_eq!(expected, result);
    }

    #[test]
    fn all_ok() {
        let a = Email {
            to: "Tom@mail.com",
            from: "Mary@mail.com",
            subject: "good morning",
            body: "Isn't it a lovely morning?!",
        };

        let result = validate_email(a);
        let expected = Ok(ValidatedEmail {
            to: "Tom@mail.com",
            from: "Mary@mail.com",
            subject: "good morning",
            body: "Isn't it a lovely morning?!",
        });

        assert_eq!(expected, result);
    }
}