rubric 0.11.0

A crate to help grade labs and assignments
Documentation
// internal uses
use crate::{TestData, Criterion};


/// A builder struct that builds a Criterion. You should create one
/// of these through [`Criterion::new`](crate::criterion::Criterion::new)
/// instead of directly.
pub struct CriterionBuilder {
    name: String,
    stub: Option<String>,
    worth: i16,
    messages: (String, String),
    desc: Option<String>,
    test: Option<Box<dyn Fn(&TestData) -> bool>>,
    index: i64,
    hide: bool
}

impl CriterionBuilder {
    /// Creates a new CriterionBuilder
    ///
    /// ```rust
    /// # use rubric::criterion_builder::CriterionBuilder;
    /// let builder = CriterionBuilder::new("my crit").build();
    /// ```
    pub fn new(name: &str) -> Self {
        CriterionBuilder {
            name: String::from(name.trim()),
            stub: None,
            worth: 0,
            messages: ("passed".to_string(), "failed".to_string()),
            desc: None,
            test: None,
            index: 100,
            hide: false
        }
    }

    /// Sets the stub of a criterion. It's like an identifier.
    ///
    /// A stub should be lowercase and not contain whitespace, it should
    /// also be unique among criteria.
    ///
    /// ```rust
    /// # use rubric::criterion_builder::CriterionBuilder;
    /// let crit = CriterionBuilder::new("my crit")
    ///     .stub("my-stub")
    ///     .build();
    /// ```
    pub fn stub(mut self, stub: &str) -> Self {
        self.stub = Some(String::from(stub));
        self
    }

    /// Sets the index
    pub fn index(mut self, index: i64) -> Self {
        self.index = index;
        self
    }

    /// Attaches a test.
    ///
    /// ```rust
    /// # use rubric::criterion_builder::CriterionBuilder;
    /// # use rubric::TestData;
    /// fn my_test(_: &TestData) -> bool {
    ///     true
    /// }
    ///
    /// let crit = CriterionBuilder::new("my crit")
    ///     .test(Box::new(my_test))
    ///     .build();
    /// ```
    pub fn test(mut self,
        test: Box<dyn Fn(&TestData) -> bool>) -> Self {
        self.test = Some(test);
        self
    }

    /// Sets the messages of a criterion.
    ///
    /// ```rust
    /// # use rubric::criterion_builder::CriterionBuilder;
    /// let crit = CriterionBuilder::new("my crit")
    ///     .messages("passed", "failed")
    ///     .build();
    /// ```
    pub fn messages(mut self, success: &str, failure: &str) -> Self {
        self.messages = (
            String::from(success),
            String::from(failure)
        );
        self
    }

    /// Sets the description of a criterion. It should be
    /// relatively short.
    ///
    /// ```rust
    /// # use rubric::criterion_builder::CriterionBuilder;
    /// let crit = CriterionBuilder::new("Git installed")
    ///     .desc("Tests that Git is installed")
    ///     .build();
    /// ```
    pub fn desc(mut self, desc: &str) -> Self {
        self.desc = Some(String::from(desc));
        self
    }

    /// Sets the worth on a Criterion
    pub fn worth(mut self, worth: i16) -> Self {
        self.worth = worth;
        self
    }

    /// Sets the hide flag on a criterion. If hide is true,
    /// the criterion can't be printed.
    ///
    /// ```rust
    /// # use rubric::criterion_builder::CriterionBuilder;
    /// let crit = CriterionBuilder::new("my crit")
    ///     .hide(true)
    ///     .build();
    /// ```
    pub fn hide(mut self, hide: bool) -> Self {
        self.hide = hide;
        self
    }

    /// Finalizes the criterion.
    ///
    /// If a stub wasn't manually set, it will create one based on the
    /// name. It will lowercase it, then replace all whitespace with dashes.
    ///
    /// ```rust
    /// # use rubric::criterion_builder::CriterionBuilder;
    /// let crit = CriterionBuilder::new("my crit")
    ///     // more confiuration options...
    ///     .build();
    /// ```
    pub fn build(self) -> Criterion {

        let name = self.name;
        let stub = self.stub.unwrap_or_else(|| {
            name.to_lowercase()
                .split_whitespace()
                .collect::<Vec<_>>()
                .join("-")
        });

        Criterion {
            stub: stub,
            name: name,
            worth: self.worth,
            messages: self.messages,
            desc: self.desc,
            test: self.test.unwrap_or(Box::new(|_: &TestData| false)),
            index: self.index,
            status: None,
            hide: self.hide
        }
    }
}




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

    #[test]
    fn test_with_name() {
        let cb = CriterionBuilder::new("my crit");
        assert_eq!(cb.name, "my crit");
        assert_eq!(cb.worth, 0);

        assert_eq!(cb.messages, (
            "passed".to_string(),
            "failed".to_string()
        ));

        assert!(cb.stub.is_none());
        assert!(cb.desc.is_none());
        assert!(cb.test.is_none());
        assert!(!cb.hide);
    }

    #[test]
    fn test_default_values() {
        let crit = CriterionBuilder::new("My Crit").build();
        assert_eq!(crit.name, "My Crit");
        assert_eq!(crit.stub, "my-crit");
        assert_eq!(crit.messages.0, "passed");
        assert_eq!(crit.messages.1, "failed");
        assert!(crit.desc.is_none());
        assert!(!crit.hide);
    }

    #[test]
    fn test_build_parameters() {
        let crit = CriterionBuilder::new("My crit")
            .stub("my-stub")
            .messages("success", "failed :(")
            .desc("Here's my desc")
            .hide(true)
            .build();

        assert_eq!(crit.name, "My crit");
        assert_eq!(crit.stub, "my-stub");
        assert_eq!(crit.messages, (
            "success".to_string(),
            "failed :(".to_string()
        ));
        assert_eq!(crit.desc.unwrap(), "Here's my desc");
        assert!(crit.hide);

    }

    #[test]
    fn test_build() {
        let cb = CriterionBuilder::new("my crit");
        let crit = cb.build();
        assert_eq!(crit.name, "my crit");
    }

    #[test]
    fn test_default_stub() {
        let crit = CriterionBuilder::new("My crit 1").build();
        assert_eq!(crit.stub, "my-crit-1");

        let crit2 = CriterionBuilder::new("MY CRIT    2").build();
        assert_eq!(crit2.stub, "my-crit-2");
    }
}