rschema_core/schema.rs
1use serde::Serialize;
2
3use std::fs;
4
5use crate::{
6 Draft,
7 Result,
8 Schematic,
9};
10
11mod defs;
12pub mod r#type;
13
14pub use defs::Definitions;
15pub use r#type::Type;
16
17/// This is a structure representing the JSON schema itself.
18///
19/// ## Create Schema
20///
21/// See [the documentation top](https://docs.rs/rschema/latest/rschema/) for usage.
22///
23/// ## To JSON schema string
24///
25/// ```
26/// #[derive(Debug, Schematic)]
27/// struct Example {
28/// #[rschema(field(
29/// title = "Dummy",
30/// description = "Dummy field",
31/// ))]
32/// dummy: String,
33/// }
34///
35/// fn main() -> rschema::Result<()> {
36/// let schema_str = Schema::new::<Example>("Example")
37/// .to_string()?;
38///
39/// assert_eq!(
40/// schema_str,
41/// r#"{"title":"Example","type":"object","properties":{"dummy":{"title":"Dummy","description":"Dummy field","type":"string"}},"additionalProperties":false}"#
42/// );
43///
44/// Ok(())
45/// }
46/// ```
47///
48/// Use [`to_string_pretty`](fn@Schema::to_string_pretty) to generate as a pretty-prited string.
49///
50///
51#[derive(Debug, Serialize)]
52pub struct Schema {
53 #[serde(rename = "$schema")]
54 #[serde(skip_serializing_if = "Option::is_none")]
55 pub schema: Option<Draft>,
56
57 #[serde(rename = "$id")]
58 #[serde(skip_serializing_if = "Option::is_none")]
59 pub id: Option<String>,
60
61 pub title: String,
62
63 #[serde(skip_serializing_if = "Option::is_none")]
64 pub description: Option<String>,
65
66 #[serde(flatten)]
67 ty: Type,
68
69 #[serde(rename = "$defs")]
70 #[serde(skip_serializing_if = "Definitions::is_empty")]
71 defs: Definitions,
72}
73
74impl Schema {
75 /// Create a schema object from the given type `T`.
76 ///
77 pub fn new<T: Schematic>(title: &str) -> Self {
78 Schema {
79 schema: None,
80 id: None,
81 title: title.into(),
82 description: None,
83 ty: T::__type_no_attr(),
84 defs: T::__defs(),
85 }
86 }
87
88 /// Add a description about this schema.
89 ///
90 pub fn description(
91 &mut self,
92 description: impl Into<String>,
93 ) -> &mut Self {
94 self.description = Some(description.into());
95 self
96 }
97
98 /// Specify `$schema`.
99 ///
100 pub fn schema(
101 &mut self,
102 schema: Draft,
103 ) -> &mut Self {
104 self.schema = Some(schema);
105 self
106 }
107
108 /// Specify `$id`.
109 ///
110 pub fn id(
111 &mut self,
112 id: impl Into<String>,
113 ) -> &mut Self {
114 self.id = Some(id.into());
115 self
116 }
117
118 /// Generate a JSON schema string.
119 ///
120 /// # Errors
121 ///
122 /// Internally calls `serde_json::to_string`, so this can fail if it fails. [Read more](https://docs.rs/serde_json/latest/serde_json/fn.to_string.html)
123 ///
124 pub fn to_string(&self) -> Result<String> {
125 let schema_str = serde_json::to_string(self)?;
126 Ok(schema_str)
127 }
128
129 /// Generate a pretty-printed JSON schema string.
130 ///
131 /// # Errors
132 ///
133 /// Internally calls `serde_json::to_string_pretty`, so this can fail if it fails. [Read more](https://docs.rs/serde_json/latest/serde_json/fn.to_string_pretty.html)
134 ///
135 pub fn to_string_pretty(&self) -> Result<String> {
136 let schema_str = serde_json::to_string_pretty(self)?;
137 Ok(schema_str)
138 }
139
140 /// Write a JSON schema string to a file.
141 ///
142 /// # Errors
143 ///
144 /// This call can fail if its own `to_string` call or writing to a file fails.
145 ///
146 pub fn write(
147 &self,
148 path: impl AsRef<std::path::Path>,
149 ) -> Result<()> {
150 let schema_str = self.to_string()?;
151 fs::write(path, schema_str)?;
152
153 Ok(())
154 }
155
156 /// Write a pretty-printed JSON schema string to a file.
157 ///
158 /// # Errors
159 ///
160 /// This call can fail if its own `to_string_pretty` call or writing to a file fails.
161 ///
162 pub fn write_pretty(
163 &self,
164 path: impl AsRef<std::path::Path>,
165 ) -> Result<()> {
166 let schema_str = self.to_string_pretty()?;
167 fs::write(path, schema_str)?;
168
169 Ok(())
170 }
171}