use super::context::{LoadContext, SaveContext};
#[derive(Debug, Clone)]
pub enum PropertyKind {
Array {
items: serde_json::Value,
},
Object {
properties: serde_json::Value,
},
Custom {
kind_name: String,
},
}
impl Default for PropertyKind {
fn default() -> Self {
PropertyKind::Custom {
kind_name: String::new(),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct Property {
pub name: String,
pub description: Option<String>,
pub required: Option<bool>,
pub default: Option<serde_json::Value>,
pub example: Option<serde_json::Value>,
pub enum_values: Option<Vec<serde_json::Value>>,
pub kind: PropertyKind,
}
impl Property {
pub fn new() -> Self {
Self::default()
}
pub fn from_json(json: &str, ctx: &LoadContext) -> Result<Self, serde_json::Error> {
let value: serde_json::Value = serde_json::from_str(json)?;
Ok(Self::load_from_value(&value, ctx))
}
pub fn from_yaml(yaml: &str, ctx: &LoadContext) -> Result<Self, serde_yaml::Error> {
let value: serde_json::Value = serde_yaml::from_str(yaml)?;
Ok(Self::load_from_value(&value, ctx))
}
pub fn load_from_value(value: &serde_json::Value, ctx: &LoadContext) -> Self {
let value = ctx.process_input(value.clone());
if let Some(value) = value.as_bool() {
let expansion = serde_json::json!({"kind":"boolean","example":value});
return Self::load_from_value(&expansion, ctx);
}
if let Some(value) = value.as_f64() {
let expansion = serde_json::json!({"kind":"float","example":value});
return Self::load_from_value(&expansion, ctx);
}
if let Some(value) = value.as_i64() {
let expansion = serde_json::json!({"kind":"integer","example":value});
return Self::load_from_value(&expansion, ctx);
}
if let Some(s) = value.as_str() {
let value = s.to_string();
let expansion = serde_json::json!({"kind":"string","example":value});
return Self::load_from_value(&expansion, ctx);
}
let kind_str = value.get("kind").and_then(|v| v.as_str()).unwrap_or("");
let kind = match kind_str {
"array" => PropertyKind::Array {
items: value
.get("items")
.cloned()
.unwrap_or(serde_json::Value::Null),
},
"object" => PropertyKind::Object {
properties: value
.get("properties")
.cloned()
.unwrap_or(serde_json::Value::Null),
},
_ => PropertyKind::Custom {
kind_name: kind_str.to_string(),
},
};
Self {
name: value
.get("name")
.and_then(|v| v.as_str())
.unwrap_or_default()
.to_string(),
description: value
.get("description")
.and_then(|v| v.as_str())
.map(|s| s.to_string()),
required: value.get("required").and_then(|v| v.as_bool()),
default: value.get("default").cloned(),
example: value.get("example").cloned(),
enum_values: value
.get("enumValues")
.and_then(|v| v.as_array())
.map(|arr| arr.to_vec()),
kind,
}
}
pub fn kind_str(&self) -> &str {
match &self.kind {
PropertyKind::Array { .. } => "array",
PropertyKind::Object { .. } => "object",
PropertyKind::Custom { kind_name, .. } => kind_name.as_str(),
}
}
pub fn to_value(&self, ctx: &SaveContext) -> serde_json::Value {
let mut result = serde_json::Map::new();
result.insert(
"kind".to_string(),
serde_json::Value::String(self.kind_str().to_string()),
);
if !self.name.is_empty() {
result.insert(
"name".to_string(),
serde_json::Value::String(self.name.clone()),
);
}
if let Some(ref val) = self.description {
result.insert(
"description".to_string(),
serde_json::Value::String(val.clone()),
);
}
if let Some(val) = self.required {
result.insert("required".to_string(), serde_json::Value::Bool(val));
}
if let Some(ref val) = self.default {
result.insert(
"default".to_string(),
serde_json::to_value(val).unwrap_or(serde_json::Value::Null),
);
}
if let Some(ref val) = self.example {
result.insert(
"example".to_string(),
serde_json::to_value(val).unwrap_or(serde_json::Value::Null),
);
}
if let Some(ref items) = self.enum_values {
result.insert(
"enumValues".to_string(),
serde_json::to_value(items).unwrap_or(serde_json::Value::Null),
);
}
match &self.kind {
PropertyKind::Array { items, .. } => {
if !items.is_null() {
result.insert("items".to_string(), items.clone());
}
}
PropertyKind::Object { properties, .. } => {
if !properties.is_null() {
result.insert("properties".to_string(), properties.clone());
}
}
PropertyKind::Custom { kind_name: _, .. } => {}
}
ctx.process_dict(serde_json::Value::Object(result))
}
pub fn to_json(&self, ctx: &SaveContext) -> Result<String, serde_json::Error> {
serde_json::to_string_pretty(&self.to_value(ctx))
}
pub fn to_yaml(&self, ctx: &SaveContext) -> Result<String, serde_yaml::Error> {
serde_yaml::to_string(&self.to_value(ctx))
}
}