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}