use serde::Serialize;
pub use schemars::JsonSchema;
#[derive(Clone, Debug)]
pub struct Schema(pub(crate) schemars::Schema);
impl Schema {
#[must_use]
pub fn for_type<T: JsonSchema>() -> Self {
Self(schemars::SchemaGenerator::default().into_root_schema_for::<T>())
}
#[must_use]
pub fn empty() -> Self {
Self::for_type::<()>()
}
pub fn try_from_value(value: serde_json::Value) -> Result<Self, SchemaError> {
schemars::Schema::try_from(value)
.map(Self)
.map_err(|e| SchemaError::InvalidSchema(e.to_string()))
}
#[must_use]
pub fn to_value(&self) -> serde_json::Value {
serde_json::to_value(&self.0).unwrap_or_default()
}
pub fn to_json_string(&self) -> Result<String, serde_json::Error> {
serde_json::to_string(&self.0)
}
pub fn to_json_string_pretty(&self) -> Result<String, serde_json::Error> {
serde_json::to_string_pretty(&self.0)
}
}
impl Serialize for Schema {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.0.serialize(serializer)
}
}
impl From<schemars::Schema> for Schema {
fn from(schema: schemars::Schema) -> Self {
Self(schema)
}
}
impl Default for Schema {
fn default() -> Self {
Self::empty()
}
}
#[derive(Debug, thiserror::Error)]
pub enum SchemaError {
#[error("invalid schema: {0}")]
InvalidSchema(String),
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
use serde::Deserialize;
use serde_json::json;
#[derive(Deserialize, JsonSchema)]
#[allow(dead_code)]
struct TestArgs {
name: String,
#[serde(default)]
count: Option<i32>,
}
#[test]
fn test_schema_for_type() {
let schema = Schema::for_type::<TestArgs>();
let value = schema.to_value();
assert!(value.get("properties").is_some());
let props = value.get("properties").unwrap();
assert!(props.get("name").is_some());
assert!(props.get("count").is_some());
}
#[test]
fn test_schema_empty() {
let schema = Schema::empty();
let value = schema.to_value();
assert!(value.is_object() || value.is_boolean());
}
#[test]
fn test_schema_try_from_value() {
let schema = Schema::try_from_value(json!({
"type": "object",
"properties": {
"message": { "type": "string" }
}
}));
assert!(schema.is_ok());
}
#[test]
fn test_schema_to_json_string() {
let schema = Schema::for_type::<TestArgs>();
let json_str = schema.to_json_string();
assert!(json_str.is_ok());
assert!(json_str.unwrap().contains("properties"));
}
#[test]
fn test_schema_default() {
let schema = Schema::default();
let _ = schema.to_value();
}
}