shuyi/lib.rs
1//! Shuyi Engine, by its front door (ADR-0009 ยง6). A game depends on this
2//! crate alone and turns on the features of the modules it wants, each
3//! off by default; `docs/modules.md` lists them by concern, and `shuyi
4//! add <module>` turns one on.
5//!
6//! - [`core`], [`assets`], [`ui`], [`platform`] and [`app`]: the default
7//! engine's crates, `shuyi-core` to `shuyi-app`, under short names
8//! (`app`, the macroquad backend, on every target but WASI, which has no
9//! window, and with its feature, on by default; a game on a device without
10//! `std` turns the default features off);
11//! - [`prelude`]: what a game on the short path names, with
12//! [`run_game`](prelude::run_game);
13//! - [`enabled_modules`]: the [`Module`] of each module turned on, which
14//! the game installs once.
15#![cfg_attr(not(feature = "std"), no_std)]
16
17extern crate alloc;
18
19#[cfg(all(feature = "app", not(target_os = "wasi")))]
20pub use shuyi_app as app;
21pub use shuyi_assets as assets;
22pub use shuyi_core as core;
23pub use shuyi_platform as platform;
24pub use shuyi_ui as ui;
25
26use alloc::boxed::Box;
27use alloc::vec::Vec;
28
29use shuyi_ui::GameData;
30use shuyi_ui::module::Module;
31
32/// What a game on the short path names (ADR-0036): `shuyi-platform`'s
33/// prelude, and [`run_game`](shuyi_app::run_game), which runs it from a
34/// plain `main`.
35pub mod prelude {
36 #[cfg(all(feature = "app", not(target_os = "wasi")))]
37 pub use shuyi_app::run_game;
38 pub use shuyi_platform::prelude::*;
39}
40
41/// The [`Module`] of each module whose feature is on, made with its
42/// defaults, in the catalog's order: empty with no feature on. A game
43/// installs them once, with [`Game::with_modules`](shuyi_ui::Game::with_modules)
44/// or [`BasicGame::modules`](shuyi_platform::BasicGame::modules), so
45/// turning a module on needs no change to its code. A module that needs
46/// arguments is not here: the game installs it, as its README says.
47pub fn enabled_modules<G: GameData>() -> Vec<Box<dyn Module<G>>> {
48 // A module's ticket adds its line here, behind its feature:
49 // `#[cfg(feature = "<name>")] modules.push(Box::new(...));`.
50 Vec::new()
51}
52
53#[cfg(test)]
54mod tests {
55 use shuyi_ui::toy::{Toy, game};
56
57 use super::enabled_modules;
58
59 #[test]
60 fn every_module_turned_on_installs_beside_the_others() {
61 // With every feature on (CI's modules job), the engine's modules
62 // have names and states of their own: none is a `ModuleError`.
63 let modules = enabled_modules::<Toy>();
64 let names: Vec<&str> = modules.iter().map(|m| m.name()).collect();
65 let game = game()
66 .with_modules(modules)
67 .unwrap_or_else(|e| panic!("{e}"));
68 let installed: Vec<&str> = game.modules().iter().map(|m| m.name).collect();
69 assert_eq!(installed, names);
70 }
71
72 #[test]
73 #[cfg(all(feature = "app", not(target_os = "wasi")))]
74 fn the_prelude_names_the_short_path_and_run_game() {
75 use crate::prelude::{BasicGame, UiAction, run_game};
76 let _: fn(BasicGame<UiAction>) = run_game::<UiAction, ()>;
77 }
78}