use crate::CUSIP;
use schemars::{
gen::SchemaGenerator,
schema::{InstanceType, Schema, SchemaObject, SingleOrVec, StringValidation},
JsonSchema,
};
impl JsonSchema for CUSIP {
fn schema_name() -> String {
"CUSIP".to_string()
}
fn json_schema(_gen: &mut SchemaGenerator) -> Schema {
let schema = SchemaObject {
instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))),
string: Some(Box::new(StringValidation {
pattern: Some("^[0-9A-Z]{9}$".to_string()),
..Default::default()
})),
..Default::default()
};
schema.into()
}
}
#[cfg(test)]
mod tests {
use super::*;
use schemars::schema_for;
#[test]
fn schema_generation() {
let schema = schema_for!(CUSIP);
let schema_json = serde_json::to_value(&schema).unwrap();
assert_eq!(schema_json["title"], "CUSIP");
}
#[test]
fn schema_validation() {
let schema = schema_for!(CUSIP);
let compiled = jsonschema::Validator::new(&serde_json::to_value(&schema).unwrap()).unwrap();
let valid_values = vec![
serde_json::json!("09739D100"), serde_json::json!("023135106"), serde_json::json!("S08000AA9"), serde_json::json!("837649128"), ];
for value in valid_values {
let result = compiled.validate(&value);
assert!(result.is_ok(), "Expected {:?} to be valid", value);
}
let invalid_values = vec![
serde_json::json!("09739D10"), serde_json::json!("09739D1000"), serde_json::json!("09739d100"), serde_json::json!("09739D10!"), serde_json::json!(""), serde_json::json!(123456789), serde_json::json!(null), ];
for value in invalid_values {
let result = compiled.validate(&value);
assert!(result.is_err(), "Expected {:?} to be invalid", value);
}
}
#[cfg(feature = "serde")]
#[test]
fn schema_matches_serde() {
use serde_json::Value;
let schema = schema_for!(CUSIP);
let compiled = jsonschema::Validator::new(&serde_json::to_value(&schema).unwrap()).unwrap();
let test_cases = vec![
"09739D100", "023135106", "S08000AA9", "837649128", ];
for cusip_str in test_cases {
let cusip: CUSIP =
serde_json::from_value(Value::String(cusip_str.to_string())).unwrap();
let serialized = serde_json::to_value(cusip).unwrap();
let result = compiled.validate(&serialized);
assert!(
result.is_ok(),
"Schema should accept serde-serialized CUSIP: {}",
cusip_str
);
}
}
#[test]
fn struct_with_cusip_roundtrip() {
#[derive(schemars::JsonSchema)]
#[allow(dead_code)]
struct Security {
cusip: CUSIP,
name: String,
}
let schema = schema_for!(Security);
let compiled = jsonschema::Validator::new(&serde_json::to_value(&schema).unwrap()).unwrap();
let valid_security = serde_json::json!({
"cusip": "09739D100",
"name": "Boise Cascade"
});
let result = compiled.validate(&valid_security);
assert!(result.is_ok(), "Schema should accept valid Security object");
let invalid_security = serde_json::json!({
"cusip": "invalid",
"name": "Test Corp"
});
let result = compiled.validate(&invalid_security);
assert!(
result.is_err(),
"Schema should reject Security with invalid CUSIP"
);
}
}