//! Content: the cards and the Baba Is You words. Only this directory (and the card data in `cards/`) may name //! specific cards, or specific Baba Is You nouns. //! //! From Module 5 every card is a JSON file in `cards/` at the crate root, in the //! course's card format (`course/schema/card.schema.json`). The functions that //! were card effects in Modules 2–4 are gone: the engine interprets the data. //! `start-module` copies the course's card files into `cards/`; add your own there. use std::path::Path; use std::sync::OnceLock; use crate::engine::CardDef; mod movement; pub use movement::responses as movement; /// Every card in `cards/`, in file-name order. Read once per test run. pub fn cards() -> Vec { static CARDS: OnceLock> = OnceLock::new(); CARDS .get_or_init(|| load_dir(&Path::new(env!("CARGO_MANIFEST_DIR")).join("cards"))) .clone() } /// Parse every `.json` file in `dir` as a card definition. pub fn load_dir(dir: &Path) -> Vec { let entries = std::fs::read_dir(dir) .unwrap_or_else(|e| panic!("cannot read card directory {}: {e}", dir.display())); let mut paths: Vec<_> = entries .map(|entry| entry.expect("card directory entry").path()) .filter(|p| p.extension().and_then(|x| x.to_str()) == Some("json")) .collect(); paths.sort(); paths .iter() .map(|path| { let text = std::fs::read_to_string(path) .unwrap_or_else(|e| panic!("cannot read {}: {e}", path.display())); serde_json::from_str(&text) .unwrap_or_else(|e| panic!("{} is not a valid card: {e}", path.display())) }) .collect() } /// Part II: the words of Baba Is You the parser can read, besides the engine's own /// (`IS`, `AND`, `NOT`, `TEXT`). Nouns and properties are data: a new word is one /// more entry in `words.json` in this directory. pub fn words() -> Vec { serde_json::from_str(include_str!("words.json")) .expect("src/content/words.json is not a word list") }