Skip to main content

sqlite_graphrag/
print_schema.rs

1//! Agent-native R-AN-01: emit a command's JSON Schema and exit without work.
2//!
3//! When a subcommand is invoked with `--print-schema`, the handler must write
4//! the corresponding file from `docs/schemas/*.schema.json` to stdout as
5//! **compact** JSON and return successfully without opening the database,
6//! calling an LLM, or performing other side effects.
7//!
8//! Schemas are embedded at compile time so the installed binary does not
9//! depend on a source-tree checkout.
10
11use crate::errors::AppError;
12use crate::output;
13
14/// Identifiers for schemas that currently support `--print-schema`.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum SchemaId {
17    /// `list` → `docs/schemas/list.schema.json`
18    List,
19    /// `recall` → `docs/schemas/recall.schema.json`
20    Recall,
21    /// `enrich --status` → `docs/schemas/enrich-status.schema.json`
22    EnrichStatus,
23    /// `config list` → `docs/schemas/config-list.schema.json`
24    ConfigList,
25}
26
27impl SchemaId {
28    /// Human-readable command name for error messages.
29    pub const fn name(self) -> &'static str {
30        match self {
31            Self::List => "list",
32            Self::Recall => "recall",
33            Self::EnrichStatus => "enrich-status",
34            Self::ConfigList => "config-list",
35        }
36    }
37
38    /// Embedded pretty JSON Schema source (from the repository at build time).
39    const fn embedded(self) -> &'static str {
40        match self {
41            Self::List => include_str!("../docs/schemas/list.schema.json"),
42            Self::Recall => include_str!("../docs/schemas/recall.schema.json"),
43            Self::EnrichStatus => include_str!("../docs/schemas/enrich-status.schema.json"),
44            Self::ConfigList => include_str!("../docs/schemas/config-list.schema.json"),
45        }
46    }
47}
48
49/// Emit the compact JSON Schema for `id` to stdout and flush.
50///
51/// Stdout contains only the schema document (one compact JSON line) so agents
52/// can pipe the output into a validator without filtering tracing noise.
53///
54/// # Errors
55/// Returns [`AppError::Validation`] if the embedded schema is not valid JSON
56/// (should never happen for the checked-in files) or an I/O error from stdout.
57pub fn emit(id: SchemaId) -> Result<(), AppError> {
58    let value: serde_json::Value = serde_json::from_str(id.embedded()).map_err(|e| {
59        AppError::Validation(crate::i18n::validation::embedded_schema_invalid_json(
60            id.name(),
61            &e,
62        ))
63    })?;
64    // Compact form: strip pretty-print whitespace from the source files.
65    output::emit_json_compact(&value)
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn embedded_schemas_parse_as_json_objects() {
74        for id in [
75            SchemaId::List,
76            SchemaId::Recall,
77            SchemaId::EnrichStatus,
78            SchemaId::ConfigList,
79        ] {
80            let v: serde_json::Value = serde_json::from_str(id.embedded())
81                .unwrap_or_else(|e| panic!("{}: {e}", id.name()));
82            assert!(v.is_object(), "{} schema must be a JSON object", id.name());
83            assert!(
84                v.get("$schema").is_some() || v.get("type").is_some(),
85                "{} schema must look like a JSON Schema document",
86                id.name()
87            );
88        }
89    }
90}