Skip to main content

cli/
preset_schema.rs

1use crate::commands::{Cli, PresetReportFormat};
2use anyhow::{Context, Result};
3use clap::CommandFactory;
4use serde::Serialize;
5use serde_json::Value;
6use std::collections::BTreeMap;
7
8#[derive(Serialize)]
9struct PresetCommandHelpV1 {
10    name: String,
11    help: String,
12}
13
14#[derive(Serialize)]
15struct PresetSchemaDocumentV1 {
16    schema_version: u32,
17    commands: Vec<PresetCommandHelpV1>,
18    schemas: BTreeMap<String, Value>,
19}
20
21pub fn handle_schema(format: PresetReportFormat) -> Result<()> {
22    let core = shine_core::runtime::preset_schema_reference_v1();
23    let document = PresetSchemaDocumentV1 {
24        schema_version: core.schema_version,
25        commands: generated_command_help()?,
26        schemas: core.schemas,
27    };
28    match format {
29        PresetReportFormat::Json => println!("{}", serde_json::to_string_pretty(&document)?),
30        PresetReportFormat::Text => {
31            println!(
32                "Preset authoring schema reference v{}",
33                document.schema_version
34            );
35            println!("Commands:");
36            for command in &document.commands {
37                println!("  {}", command.name);
38            }
39            println!("Schemas:");
40            for name in document.schemas.keys() {
41                println!("  {name}");
42            }
43            println!("Use --format json for generated command help and JSON Schemas.");
44        }
45    }
46    Ok(())
47}
48
49fn generated_command_help() -> Result<Vec<PresetCommandHelpV1>> {
50    let root = Cli::command();
51    let preset = root
52        .find_subcommand("preset")
53        .context("CLI does not contain the preset command")?;
54    let mut output = Vec::new();
55    for name in [
56        "validate", "lint", "plan", "test", "pack", "migrate", "schema",
57    ] {
58        let mut command = preset
59            .find_subcommand(name)
60            .with_context(|| format!("preset command is missing {name}"))?
61            .clone()
62            .bin_name(format!("shine preset {name}"));
63        let mut buffer = Vec::new();
64        command.write_long_help(&mut buffer)?;
65        output.push(PresetCommandHelpV1 {
66            name: format!("shine preset {name}"),
67            help: String::from_utf8(buffer).context("generated command help was not UTF-8")?,
68        });
69    }
70    Ok(output)
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn schema_reference_uses_live_clap_help() {
79        let commands = generated_command_help().unwrap();
80        assert_eq!(commands.len(), 7);
81        assert!(commands[2].help.contains("--platform"));
82        assert!(commands[3].help.contains("shine.test.toml"));
83    }
84}