spacetimedb_cli/subcommands/
describe.rs1use crate::api::ClientApi;
2use crate::common_args;
3use crate::config::Config;
4use crate::sql::parse_req;
5use crate::util::UNSTABLE_WARNING;
6use anyhow::Context;
7use clap::{Arg, ArgAction, ArgMatches};
8use spacetimedb_lib::sats;
9
10pub fn cli() -> clap::Command {
11 clap::Command::new("describe")
12 .about(format!(
13 "Describe the structure of a database or entities within it. {UNSTABLE_WARNING}"
14 ))
15 .arg(
16 Arg::new("database")
17 .required(true)
18 .help("The name or identity of the database to describe"),
19 )
20 .arg(
21 Arg::new("entity_type")
22 .value_parser(clap::value_parser!(EntityType))
23 .requires("entity_name")
24 .help("Whether to describe a reducer or table"),
25 )
26 .arg(
27 Arg::new("entity_name")
28 .requires("entity_type")
29 .help("The name of the entity to describe"),
30 )
31 .arg(
32 Arg::new("json")
33 .long("json")
34 .action(ArgAction::SetTrue)
35 .required(true)
37 .help(
38 "Output the schema in JSON format. Currently required; in the future, omitting this will \
39 give human-readable output.",
40 ),
41 )
42 .arg(common_args::anonymous())
43 .arg(common_args::server().help("The nickname, host name or URL of the server hosting the database"))
44 .arg(common_args::yes())
45 .after_help("Run `spacetime help describe` for more detailed information.\n")
46}
47
48#[derive(clap::ValueEnum, Clone, Copy)]
49enum EntityType {
50 Reducer,
51 Table,
52}
53
54pub async fn exec(config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {
55 eprintln!("{UNSTABLE_WARNING}\n");
56
57 let entity_name = args.get_one::<String>("entity_name");
58 let entity_type = args.get_one::<EntityType>("entity_type");
59 let entity = entity_type.zip(entity_name);
60 let json = args.get_flag("json");
61
62 let conn = parse_req(config, args).await?;
63 let api = ClientApi::new(conn);
64
65 let module_def = api.module_def().await?;
66
67 if json {
68 fn sats_to_json<T: sats::Serialize>(v: &T) -> serde_json::Result<String> {
69 serde_json::to_string_pretty(sats::serde::SerdeWrapper::from_ref(v))
70 }
71 let json = match entity {
72 Some((EntityType::Reducer, reducer_name)) => {
73 let reducer = module_def
74 .reducers
75 .iter()
76 .find(|r| *r.name == **reducer_name)
77 .context("no such reducer")?;
78 sats_to_json(reducer)?
79 }
80 Some((EntityType::Table, table_name)) => {
81 let table = module_def
82 .tables
83 .iter()
84 .find(|t| *t.name == **table_name)
85 .context("no such table")?;
86 sats_to_json(table)?
87 }
88 None => sats_to_json(&module_def)?,
89 };
90
91 println!("{json}");
92 } else {
93 }
95
96 Ok(())
97}