feat(cli): --demo demonstration mode flag + app plumbing (#22, ADR-0047 D1)
Add `--demo` (and the RDBMS_PLAYGROUND_DEMO env fallback) to enter demonstration mode, threaded onto App.demo_mode through run_loop — mirrors the --no-undo plumbing. Off by default, zero footprint when off. The --help line advertises only the visible keystroke badges; the Ctrl+] caption trigger is kept low-profile (ADR-0047 D6 updated). Phase A of ADR-0047; behaviour (badges/captions) lands in B and C.
This commit is contained in:
+77
@@ -42,6 +42,13 @@ pub struct Args {
|
||||
/// mode > the default (`simple`). Combines with `--resume` and
|
||||
/// a positional path; on collision the flag wins.
|
||||
pub mode: Option<Mode>,
|
||||
/// `--demo` (or `RDBMS_PLAYGROUND_DEMO` set truthy): enter
|
||||
/// **demonstration mode** (ADR-0047, issue #22). Off by default,
|
||||
/// zero footprint when off. When on, the app shows transient
|
||||
/// on-screen badges for otherwise-invisible keys (Tab, Enter, …)
|
||||
/// and enables the `Ctrl+]` stealth step-caption buffer — for
|
||||
/// screencasts and live teaching. The flag wins over the env var.
|
||||
pub demo: bool,
|
||||
}
|
||||
|
||||
/// Usage banner printed by `--help`.
|
||||
@@ -124,6 +131,12 @@ impl Args {
|
||||
let mut help = false;
|
||||
let mut no_undo = false;
|
||||
let mut mode: Option<Mode> = None;
|
||||
// Demonstration mode (ADR-0047): the env var is the default,
|
||||
// the `--demo` flag overrides it to on. Mirrors the
|
||||
// env-then-flag layering used for the log file above.
|
||||
let mut demo = env::var("RDBMS_PLAYGROUND_DEMO")
|
||||
.ok()
|
||||
.is_some_and(|v| demo_value_is_truthy(&v));
|
||||
let mut iter = iter.into_iter().map(Into::into);
|
||||
while let Some(arg) = iter.next() {
|
||||
match arg.as_str() {
|
||||
@@ -136,6 +149,9 @@ impl Args {
|
||||
"--no-undo" => {
|
||||
no_undo = true;
|
||||
}
|
||||
"--demo" => {
|
||||
demo = true;
|
||||
}
|
||||
"--theme" => {
|
||||
let value = iter.next().ok_or(ArgsError::MissingValue("theme"))?;
|
||||
theme = match value.as_str() {
|
||||
@@ -194,10 +210,25 @@ impl Args {
|
||||
help,
|
||||
no_undo,
|
||||
mode,
|
||||
demo,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a `RDBMS_PLAYGROUND_DEMO` value enables demo mode.
|
||||
///
|
||||
/// Truthy for any value except the conventional "off" set
|
||||
/// (`0`/`false`/`no`/`off`, case-insensitively, and the empty
|
||||
/// string). So `RDBMS_PLAYGROUND_DEMO=1` and `=true` enable, while
|
||||
/// `=0` / `=false` explicitly disable — letting a value of `0` turn
|
||||
/// it off even if something upstream exported the variable.
|
||||
fn demo_value_is_truthy(value: &str) -> bool {
|
||||
!matches!(
|
||||
value.trim().to_ascii_lowercase().as_str(),
|
||||
"" | "0" | "false" | "no" | "off"
|
||||
)
|
||||
}
|
||||
|
||||
fn default_theme() -> Theme {
|
||||
// NFR-7: support both backgrounds. For the walking skeleton we
|
||||
// honour an explicit `--theme` flag and the COLORFGBG env var
|
||||
@@ -391,6 +422,52 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
// ---- ADR-0047 (issue #22): --demo demonstration mode ----
|
||||
|
||||
#[test]
|
||||
fn demo_flag_parses() {
|
||||
let args = Args::parse(["--demo"]).unwrap();
|
||||
assert!(args.demo);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn demo_defaults_off() {
|
||||
// Absent `--demo` (and absent env var in the test runner),
|
||||
// demo mode is off — zero footprint for real users.
|
||||
let args = Args::parse(std::iter::empty::<&str>()).unwrap();
|
||||
assert!(!args.demo, "demo is off unless --demo or the env var is given");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn demo_flag_coexists_with_positional_path() {
|
||||
let args = Args::parse(["--demo", "/home/me/MyProject"]).unwrap();
|
||||
assert!(args.demo);
|
||||
assert_eq!(
|
||||
args.project_path.as_deref(),
|
||||
Some(std::path::Path::new("/home/me/MyProject"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn demo_flag_combines_with_resume_and_mode() {
|
||||
let args = Args::parse(["--resume", "--demo", "--mode", "advanced"]).unwrap();
|
||||
assert!(args.demo);
|
||||
assert!(args.resume);
|
||||
assert_eq!(args.mode, Some(Mode::Advanced));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn demo_env_value_truthiness() {
|
||||
// Enabling values.
|
||||
for v in ["1", "true", "TRUE", "yes", "on", "anything", " 1 "] {
|
||||
assert!(demo_value_is_truthy(v), "{v:?} should enable demo mode");
|
||||
}
|
||||
// Disabling values.
|
||||
for v in ["", " ", "0", "false", "False", "no", "off", "OFF"] {
|
||||
assert!(!demo_value_is_truthy(v), "{v:?} should not enable demo mode");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_double_dash_flag_errors_even_with_positional() {
|
||||
// Make sure the path-vs-flag distinction is robust:
|
||||
|
||||
Reference in New Issue
Block a user