Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions cli/assets/fig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,56 @@ const completionSpec: Fig.Spec = {
},
],
},
{
name: "json-schema",
description:
"Generate a JSON Schema for a CLI's config file from its usage spec",
options: [
{
name: ["-f", "--file"],
description:
'A usage spec taken in as a file, use "-" to read from stdin',
isRepeatable: false,
args: {
name: "file",
template: "filepaths",
},
},
{
name: "--out-file",
description: "Write the schema here instead of to stdout",
isRepeatable: false,
args: {
name: "out_file",
template: "filepaths",
},
},
{
name: "--spec",
description: "Raw string spec input",
isRepeatable: false,
args: {
name: "spec",
},
},
{
name: "--title",
description: "The schema's title, shown by editors",
isRepeatable: false,
args: {
name: "title",
},
},
{
name: "--url",
description: "Where the schema is published, for its `$id`",
isRepeatable: false,
args: {
name: "url",
},
},
],
},
{
name: ["manpage", "man"],
options: [
Expand Down
25 changes: 25 additions & 0 deletions cli/assets/usage.1
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ Generate Fig completion spec for Amazon Q / Fig
\fBgenerate json\fR
Outputs a usage spec in json format
.TP
\fBgenerate json\-schema\fR
Generate a JSON Schema for a CLI's config file from its usage spec
.TP
\fBgenerate manpage\fR
.RS
\fIAliases: \fRman
Expand Down Expand Up @@ -275,6 +278,28 @@ A usage spec taken in as a file, use "\-" to read from stdin
.TP
\fB\-\-spec\fR \fI<SPEC>\fR
raw string spec input
.SH "USAGE GENERATE JSON-SCHEMA"
Generate a JSON Schema for a CLI's config file from its usage spec
.PP
\fBUsage:\fR usage generate json\-schema [OPTIONS]
.PP
\fBOptions:\fR
.PP
.TP
\fB\-f, \-\-file\fR \fI<FILE>\fR
A usage spec taken in as a file, use "\-" to read from stdin
.TP
\fB\-\-out\-file\fR \fI<OUT_FILE>\fR
Write the schema here instead of to stdout
.TP
\fB\-\-spec\fR \fI<SPEC>\fR
raw string spec input
.TP
\fB\-\-title\fR \fI<TITLE>\fR
The schema's title, shown by editors
.TP
\fB\-\-url\fR \fI<URL>\fR
Where the schema is published, for its `$id`
.SH "USAGE GENERATE MANPAGE"
\fBUsage:\fR usage generate manpage [OPTIONS]
.PP
Expand Down
64 changes: 64 additions & 0 deletions cli/src/cli/generate/json_schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use std::path::PathBuf;

use miette::IntoDiagnostic;

use crate::cli::generate;
use crate::schema::{config_schema, SchemaOptions};
use crate::Result;

/// Generate a JSON Schema for a CLI's config file from its usage spec
#[derive(clap::Args)]
#[clap()]
pub struct JsonSchema {
/// A usage spec taken in as a file, use "-" to read from stdin
#[clap(short, long)]
file: Option<PathBuf>,

/// Write the schema here instead of to stdout
#[clap(long, value_hint = clap::ValueHint::FilePath)]
out_file: Option<PathBuf>,

/// raw string spec input
#[clap(long, required_unless_present = "file", overrides_with = "file")]
spec: Option<String>,

/// The schema's title, shown by editors
#[clap(long)]
title: Option<String>,

/// Where the schema is published, for its `$id`
#[clap(long)]
url: Option<String>,
}

impl JsonSchema {
pub fn run(&self) -> Result<()> {
let spec = generate::file_or_spec(&self.file, &self.spec)?;
let options = SchemaOptions {
title: self
.title
.clone()
.or_else(|| Some(format!("{} configuration", spec.name))),
url: self.url.clone(),
};
let schema = config_schema(&spec.config, &options);
// A schema with no properties and `unevaluatedProperties: false` rejects every config
// file there is, which is worse than saying there is nothing to describe. Asked of
// the schema rather than of the spec, because a spec whose settings are *all*
// `scope="env"` declares props and still has nothing a file may hold.
if schema
.get("properties")
.and_then(serde_json::Value::as_object)
.is_none_or(serde_json::Map::is_empty)
Comment thread
greptile-apps[bot] marked this conversation as resolved.
{
miette::bail!(
"this spec declares nothing a config file can hold, so there is no schema to write"
);
}
Comment thread
cursor[bot] marked this conversation as resolved.
let json = serde_json::to_string_pretty(&schema).into_diagnostic()?;
// Through the shared writer, like every other generator: it takes `-` for stdout and
// reports a broken pipe instead of panicking on one.
generate::write_or_stdout(self.out_file.as_deref(), &format!("{json}\n"))?;
Ok(())
}
}
3 changes: 3 additions & 0 deletions cli/src/cli/generate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod completion;
mod completion_init;
mod fig;
mod json;
mod json_schema;
mod manpage;
mod markdown;
mod sdk;
Expand All @@ -26,6 +27,7 @@ pub enum Command {
CompletionInit(completion_init::CompletionInit),
Fig(fig::Fig),
Json(json::Json),
JsonSchema(json_schema::JsonSchema),
Manpage(manpage::Manpage),
Markdown(markdown::Markdown),
Sdk(sdk::Sdk),
Expand All @@ -38,6 +40,7 @@ impl Generate {
Command::CompletionInit(cmd) => cmd.run(),
Command::Fig(cmd) => cmd.run(),
Command::Json(cmd) => cmd.run(),
Command::JsonSchema(cmd) => cmd.run(),
Command::Manpage(cmd) => cmd.run(),
Command::Markdown(cmd) => cmd.run(),
Command::Sdk(cmd) => cmd.run(),
Expand Down
2 changes: 2 additions & 0 deletions cli/src/command_effects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const EFFECTS: &[(&str, SpecCommandEffect)] = &[
("generate completion-init", Read),
("generate fig", Read),
("generate json", Read),
("generate json-schema", Read),
("generate manpage", Read),
("generate markdown", Read),
// The only generator whose output flag is required: it cannot print an SDK
Expand All @@ -47,6 +48,7 @@ const EFFECTS: &[(&str, SpecCommandEffect)] = &[
/// All of these redirect output that would otherwise go to stdout.
const FLAG_EFFECTS: &[(&str, &str, SpecCommandEffect)] = &[
("generate fig", "out-file", Write),
("generate json-schema", "out-file", Write),
("generate manpage", "out-file", Write),
("generate markdown", "out-dir", Write),
("generate markdown", "out-file", Write),
Expand Down
1 change: 1 addition & 0 deletions cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub use cli::Cli;
mod cli;
mod command_effects;
pub mod env;
mod schema;
mod usage_spec;

#[cfg(test)]
Expand Down
Loading
Loading