agent_playground/
schema.rs1use std::{
7 fs,
8 path::{Path, PathBuf},
9};
10
11use anyhow::{Context, Result};
12use serde::Serialize;
13
14use crate::config::{PlaygroundConfigFile, RootConfigFile};
15
16const ROOT_SCHEMA_FILE_NAME: &str = "root-config.schema.json";
17const PLAYGROUND_SCHEMA_FILE_NAME: &str = "playground-config.schema.json";
18
19pub fn write_schema_site(output_dir: &Path) -> Result<()> {
34 let schemas_dir = output_dir.join("schemas");
35 fs::create_dir_all(&schemas_dir)
36 .with_context(|| format!("failed to create {}", schemas_dir.display()))?;
37
38 write_json_file(
39 &schemas_dir.join(ROOT_SCHEMA_FILE_NAME),
40 &RootConfigFile::json_schema(),
41 )?;
42 write_json_file(
43 &schemas_dir.join(PLAYGROUND_SCHEMA_FILE_NAME),
44 &PlaygroundConfigFile::json_schema(),
45 )?;
46 fs::write(output_dir.join("index.html"), render_schema_index()).with_context(|| {
47 format!(
48 "failed to write {}",
49 output_dir.join("index.html").display()
50 )
51 })?;
52
53 Ok(())
54}
55
56pub fn default_schema_site_dir() -> PathBuf {
61 PathBuf::from("target").join("schema-site")
62}
63
64fn write_json_file<T>(path: &Path, value: &T) -> Result<()>
65where
66 T: Serialize,
67{
68 let content =
69 serde_json::to_string_pretty(value).context("failed to serialize schema to JSON")?;
70 fs::write(path, content).with_context(|| format!("failed to write {}", path.display()))
71}
72
73fn render_schema_index() -> &'static str {
74 r#"<!doctype html>
75<html lang="en">
76 <head>
77 <meta charset="utf-8">
78 <meta name="viewport" content="width=device-width, initial-scale=1">
79 <title>agent-playground JSON Schemas</title>
80 <style>
81 :root {
82 color-scheme: light dark;
83 font-family: ui-sans-serif, system-ui, sans-serif;
84 }
85 body {
86 margin: 0 auto;
87 max-width: 48rem;
88 padding: 3rem 1.25rem;
89 line-height: 1.6;
90 }
91 code {
92 font-family: ui-monospace, SFMono-Regular, monospace;
93 }
94 </style>
95 </head>
96 <body>
97 <h1>agent-playground JSON Schemas</h1>
98 <p>Generated from the Rust config file models in this repository.</p>
99 <ul>
100 <li><a href="./schemas/root-config.schema.json"><code>root-config.schema.json</code></a></li>
101 <li><a href="./schemas/playground-config.schema.json"><code>playground-config.schema.json</code></a></li>
102 </ul>
103 </body>
104</html>
105"#
106}
107
108#[cfg(test)]
109mod tests {
110 use anyhow::Result;
111 use serde_json::Value;
112 use tempfile::tempdir;
113
114 use super::{PLAYGROUND_SCHEMA_FILE_NAME, ROOT_SCHEMA_FILE_NAME, write_schema_site};
115
116 #[test]
117 fn writes_schema_site_artifacts() -> Result<()> {
118 let output_dir = tempdir()?;
119
120 write_schema_site(output_dir.path())?;
121
122 let root_schema_path = output_dir
123 .path()
124 .join("schemas")
125 .join(ROOT_SCHEMA_FILE_NAME);
126 let playground_schema_path = output_dir
127 .path()
128 .join("schemas")
129 .join(PLAYGROUND_SCHEMA_FILE_NAME);
130 let index_path = output_dir.path().join("index.html");
131
132 assert!(root_schema_path.is_file());
133 assert!(playground_schema_path.is_file());
134 assert!(index_path.is_file());
135
136 let root_schema: Value = serde_json::from_str(&std::fs::read_to_string(root_schema_path)?)?;
137 let playground_schema: Value =
138 serde_json::from_str(&std::fs::read_to_string(playground_schema_path)?)?;
139 let index_html = std::fs::read_to_string(index_path)?;
140
141 assert_eq!(root_schema["type"], Value::String("object".to_string()));
142 assert_eq!(
143 playground_schema["type"],
144 Value::String("object".to_string())
145 );
146 assert!(index_html.contains("root-config.schema.json"));
147 assert!(index_html.contains("playground-config.schema.json"));
148
149 Ok(())
150 }
151}