use crate::FieldType;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct FieldSchema {
#[serde(rename = "type")]
pub field_type: FieldType,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub primary: bool,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub generated: bool,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub required: bool,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub unique: bool,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub nullable: bool,
#[serde(default, skip_serializing_if = "Option::is_none", rename = "ref")]
pub reference: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub min: Option<serde_json::Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max: Option<serde_json::Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub format: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub values: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default: Option<serde_json::Value>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub sensitive: bool,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub search: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub items: Option<String>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub transient: bool,
}
impl FieldSchema {
pub fn is_persisted(&self) -> bool {
!self.transient
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn field_schema_minimal() {
let json = r#"{"type": "string"}"#;
let fs: FieldSchema = serde_json::from_str(json).unwrap();
assert_eq!(fs.field_type, FieldType::String);
assert!(!fs.primary);
assert!(!fs.generated);
assert!(!fs.required);
assert!(!fs.unique);
assert!(!fs.nullable);
assert!(fs.reference.is_none());
assert!(fs.min.is_none());
assert!(fs.max.is_none());
assert!(fs.format.is_none());
assert!(fs.values.is_none());
assert!(fs.default.is_none());
assert!(!fs.sensitive);
assert!(!fs.search);
assert!(fs.items.is_none());
assert!(!fs.transient);
}
#[test]
fn field_schema_full() {
let json = r#"{
"type": "enum",
"primary": false,
"generated": false,
"required": true,
"unique": false,
"nullable": false,
"values": ["admin", "member", "viewer"],
"default": "member"
}"#;
let fs: FieldSchema = serde_json::from_str(json).unwrap();
assert_eq!(fs.field_type, FieldType::Enum);
assert!(fs.required);
assert_eq!(fs.values.as_ref().unwrap().len(), 3);
assert_eq!(fs.default.as_ref().unwrap(), "member");
}
#[test]
fn field_schema_with_ref() {
let json = r#"{"type": "uuid", "ref": "organizations.id", "required": true}"#;
let fs: FieldSchema = serde_json::from_str(json).unwrap();
assert_eq!(fs.reference.as_deref(), Some("organizations.id"));
}
#[test]
fn field_schema_serde_roundtrip() {
let fs = FieldSchema {
field_type: FieldType::String,
primary: false,
generated: false,
required: true,
unique: true,
nullable: false,
reference: None,
min: Some(serde_json::json!(1)),
max: Some(serde_json::json!(200)),
format: Some("email".to_string()),
values: None,
default: None,
sensitive: false,
search: true,
items: None,
transient: false,
};
let json = serde_json::to_string(&fs).unwrap();
let back: FieldSchema = serde_json::from_str(&json).unwrap();
assert_eq!(fs, back);
}
}