#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RecipeSource {
Crate {
lib: String,
},
Overlay {
path: String,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Expectation {
pub form: usize,
pub result: String,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RecipeCard {
pub id: String,
pub book: String,
pub chapter: String,
pub chapter_title: String,
pub chapter_summary: String,
pub title: String,
pub codec: String,
pub setup: Vec<u8>,
pub purpose: String,
pub order: i64,
pub chapter_order: i64,
pub book_order: i64,
pub book_title: String,
pub book_summary: String,
pub tags: Vec<String>,
pub requires: Vec<String>,
pub expect: Vec<Expectation>,
pub source: RecipeSource,
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct CookbookView {
pub books: Vec<BookView>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BookView {
pub id: String,
pub title: String,
pub summary: String,
pub chapters: Vec<ChapterView>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ChapterView {
pub name: String,
pub title: String,
pub summary: String,
pub recipes: Vec<RecipeCard>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RecipeRun {
pub recipe: String,
pub forms: usize,
pub results: Vec<String>,
pub checks: Vec<CheckResult>,
pub ok: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CheckResult {
pub form: usize,
pub expected: String,
pub actual: String,
pub pass: bool,
}
#[cfg(test)]
mod tests {
use super::*;
fn sample_card() -> RecipeCard {
RecipeCard {
id: "numbers-f64/01-basics/add-two-numbers".to_string(),
book: "numbers-f64".to_string(),
chapter: "01-basics".to_string(),
chapter_title: "Basics".to_string(),
chapter_summary: String::new(),
title: "Add two numbers".to_string(),
codec: "lisp".to_string(),
setup: b"(+ 1 2)".to_vec(),
purpose: "Add two f64 values.".to_string(),
order: 100,
chapter_order: 100,
book_order: 200,
book_title: "Numbers (f64)".to_string(),
book_summary: String::new(),
tags: vec!["arithmetic".to_string(), "intro".to_string()],
requires: vec!["numbers-f64".to_string()],
expect: vec![Expectation {
form: 0,
result: "3".to_string(),
}],
source: RecipeSource::Crate {
lib: "numbers-f64".to_string(),
},
}
}
#[test]
fn recipe_card_round_trips_its_fields() {
let card = sample_card();
let clone = card.clone();
assert_eq!(card, clone);
assert_eq!(card.id, "numbers-f64/01-basics/add-two-numbers");
assert_eq!(card.setup, b"(+ 1 2)");
assert_eq!(card.expect[0].form, 0);
assert_eq!(card.expect[0].result, "3");
assert_eq!(
card.source,
RecipeSource::Crate {
lib: "numbers-f64".to_string()
}
);
}
#[test]
fn cookbook_view_nests_book_chapter_recipe() {
let card = sample_card();
let view = CookbookView {
books: vec![BookView {
id: card.book.clone(),
title: card.book_title.clone(),
summary: String::new(),
chapters: vec![ChapterView {
name: card.chapter.clone(),
title: card.chapter_title.clone(),
summary: String::new(),
recipes: vec![card.clone()],
}],
}],
};
assert_eq!(view.books[0].chapters[0].recipes[0], card);
assert_eq!(view.books[0].id, "numbers-f64");
}
#[test]
fn recipe_run_reports_ok() {
let run = RecipeRun {
recipe: "numbers-f64/01-basics/add-two-numbers".to_string(),
forms: 1,
results: vec!["3".to_string()],
checks: vec![CheckResult {
form: 0,
expected: "3".to_string(),
actual: "3".to_string(),
pass: true,
}],
ok: true,
};
assert!(run.ok);
assert!(run.checks[0].pass);
}
}