[][src]Struct lab_grader::criterion::Criterion

pub struct Criterion {
    pub name: String,
    pub worth: i16,
    pub messages: (&'static str, &'static str),
    pub test: Box<dyn Fn(&HashMap<String, String>) -> bool>,
    pub status: Option<bool>,
    pub hide: bool,
}

A criterion

This is the heart of the application. Each criterion is responsible for checking one thing, and one thing only. You should build a list of criteria.

A lone Criterion

A Criterion has some informational fields (name, messages), a point value (worth), a status, and most importantly a test. The test takes in a HashMap<String, String> and returns a bool. The signature of every criterion's test is always the same.

use std::collections::HashMap;
use lab_grader::*;

let mut crit = Criterion::new(
    // Name
    "My First Criterion",
    // Worth
    10,
    // Pass/Fail messages, a tuple
    ("passed", "failed"),
    // Test function, contained in a Box
    Box::new(|_: &HashMap<String, String>| -> bool {
        // test code goes here
        // determine if this should pass or fail
        true
    })
);

assert!(crit.status.is_none());
crit.test();
assert_eq!(crit.status, Some(true));

We can also extract the test into a function defined elsewhere. This just helps with organization.

fn my_test(_: &HashMap<String, String>) -> bool {
    // code here...
    true
}

fn main() {
    let mut crit = Criterion::new(
        "My Second Criterion",
        10,
        ("passed", "failed"),
        Box::new(my_test)
    );

    crit.test();
    // ...
}

We can also pass data to a criterion. This data must be a &HashMap<String, String>


fn my_test(data: &HashMap<String, String>) -> bool {
    if let Some(value) = data.get("key") {
        return value == "value"
    }
    false
}

fn main() {
    let mut crit = Criterion::new(
        "My Third Criterion",
        10,
        ("passed", "failed"),
        Box::new(my_test)
    );

    // Now we need some data to pass to the criterion
    // this crate provides a data macro that builds a HashMap
    let data = data! {
        "key" => "value"
    };
    crit.test_with_data(&data);
    assert_eq!(crit.status, Some(true));
}

Fields

name: String

A short (< 30 characters), descriptive name

worth: i16

Point value of this criterion. If it passes, this value will be added to the Submission grade.

Can be negative if you wish to subtract points. Be sure to get your logic right. This value is added to the submission grade if the test returns true.

messages: (&'static str, &'static str)

Pass or fail messages, respectively

When printing a criterion, the appropriate message will be printed. Not much use other than that.

test: Box<dyn Fn(&HashMap<String, String>) -> bool>

The criterion's test

Determines if the criterion passes or fails. This signature is required.

status: Option<bool>

If the test passed, failed, or hasn't been run.

None if it hasn't been run, Some(true) or Some(false) otherwise. If this value is Some, the test has been run.

hide: bool

Currently does nothing because i'm lazy

Methods

impl Criterion[src]

pub fn new<S: AsRef<str>>(
    name: S,
    worth: i16,
    messages: (&'static str, &'static str),
    test: Box<dyn Fn(&HashMap<String, String>) -> bool>
) -> Self
[src]

Creates a new Criterion with the given parameters.

The messages parameter should be a tuple of &str containing a success then failure message, respectively. These messages will be printed when printing the criterion.

The test parameter is a Box around a closure accepting a HashMap returning a bool. This can get a bit confusing. The test closure should return true if the criterion passes, otherwise false. The &HashMap parameter allows data from outside the closure to be passed in. The signature of the &HashMap is &HashMap<String, String>, so all keys and values must be Strings. This is done to generalize the test field, as all criteria must have the same signature.

Example

A basic criterion

use std::collections::HashMap;
use lab_grader::criterion::Criterion;

let mut c = Criterion::new(
    "A test criterion",
    10,
    ("Success!", "Failure!"),
    Box::new(|_: &HashMap<String, String>| {
        // Code to test criterion goes here,
        // and should return false or...
        true
    })
);
assert!(c.test());

A criterion with data


let mut c = Criterion::new(
    "A test criterion with data!",
    10,
    ("Success!", "Failure!"),
    Box::new(|data: &HashMap<String, String>| {
        return data["my_key"] == "my_value";
    })
);

// The above criterion takes a HashMap into it's closure,
// so we must establish the data to send into the closure
let my_data = data! {
    "my_key" => "my_value"
};

assert!(c.test_with_data(&my_data));

pub fn success_message(&self) -> &'static str[src]

Returns the success message, ie. the first message in the messages tuple

pub fn failure_message(&self) -> &'static str[src]

Returns the failure message, ie. the second message in the messages tuple

pub fn hide(&mut self)[src]

Toggles the hide field on a criterion

If hide is true, printing the criterion with the default formatter will print nothing. Good if you want a secret criterion that the students don't know about

pub fn test_with_data(&mut self, data: &HashMap<String, String>) -> bool[src]

Runs the criterion's test function with the data provided.

This is almost equivilent to calling (criterion.test)(data), but this method also sets the status of the criterion to the result of the test. You should avoid calling the test directly, and call this or the test method instead.

The criterion must be mutable to call this method, as the status is changed to the result of the test.

Example


let mut c = Criterion::new(
    "A test criterion with data!",
    10,
    ("Success!", "Failure!"),
    Box::new(|data: &HashMap<String, String>| {
        return data["my_key"] == "my_value";
    })
);

let my_data = data! {
    "my_key" => "my_value"
};

c.test_with_data(&my_data);
// It's either Some(true) or Some(false) since we've tested
assert!(c.status.is_some());

pub fn test(&mut self) -> bool[src]

Runs the criterions test and assigns the result to criterion.status.

This is equivilent to running test_with_data with an empty HashMap.

Criterion must be mutable.

Example


let mut c = Criterion::new(
    "A test criterion with data!",
    10,
    ("Success!", "Failure!"),
    Box::new(|_: &HashMap<String, String>| {
        true
    })
);

assert!(c.test());
assert!(c.status.is_some());

Trait Implementations

impl Display for Criterion[src]

Displays the results of the criterion. You should test the criterion before printing it.

Output will be aligned, as you'll normally be printing a lot of these at once.

Given a configuration with the name Test criterion, success message passed!, and failure message failed!, this is what would print:

Printed before testing

My first criterion  +**  not tested

Printed after a successful test

My first criterion  +10  passed!

Printed after a failed test

My first criterion  + 0  failed!

Auto Trait Implementations

impl !RefUnwindSafe for Criterion

impl !Send for Criterion

impl !Sync for Criterion

impl Unpin for Criterion

impl !UnwindSafe for Criterion

Blanket Implementations

impl<T> Any for T where
    T: 'static + ?Sized
[src]

impl<T, I> AsResult<T, I> for T where
    I: Input, 

impl<T> Borrow<T> for T where
    T: ?Sized
[src]

impl<T> BorrowMut<T> for T where
    T: ?Sized
[src]

impl<T> From<T> for T[src]

impl<T, U> Into<U> for T where
    U: From<T>, 
[src]

impl<T> IntoCollection<T> for T

impl<T> ToString for T where
    T: Display + ?Sized
[src]

impl<T, U> TryFrom<U> for T where
    U: Into<T>, 
[src]

type Error = Infallible

The type returned in the event of a conversion error.

impl<T, U> TryInto<U> for T where
    U: TryFrom<T>, 
[src]

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.

impl<T> Typeable for T where
    T: Any