use scirs2_io::validation::{schema_helpers, SchemaConstraint, SchemaValidator};
use serde_json::json;
use std::collections::HashMap;
use tempfile::tempdir;
#[allow(dead_code)]
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("📋 Schema-based Validation Example");
println!("==================================");
demonstrate_basic_schema_validation()?;
demonstrate_nested_schema_validation()?;
demonstrate_custom_validators()?;
demonstrate_format_validators()?;
demonstrate_file_validation()?;
demonstrate_json_schema_compatibility()?;
println!("\n✅ All schema validation demonstrations completed successfully!");
println!("💡 Schema validation ensures data integrity and structure compliance");
Ok(())
}
#[allow(dead_code)]
fn demonstrate_basic_schema_validation() -> Result<(), Box<dyn std::error::Error>> {
println!("\n🔧 Demonstrating Basic Schema Validation...");
let validator = SchemaValidator::new();
let user_schema = {
let mut properties = HashMap::new();
properties.insert(
"name".to_string(),
schema_helpers::string()
.with_constraint(SchemaConstraint::MinLength(1))
.with_constraint(SchemaConstraint::MaxLength(100))
.required(),
);
properties.insert(
"age".to_string(),
schema_helpers::integer()
.with_constraint(SchemaConstraint::MinValue(0.0))
.with_constraint(SchemaConstraint::MaxValue(150.0))
.required(),
);
properties.insert("email".to_string(), schema_helpers::email().required());
properties.insert(
"score".to_string(),
schema_helpers::number()
.with_constraint(SchemaConstraint::MinValue(0.0))
.with_constraint(SchemaConstraint::MaxValue(100.0))
.optional(),
);
schema_helpers::object(properties)
};
println!(" ✅ Testing valid user data...");
let valid_user = json!({
"name": "Alice Johnson",
"age": 30,
"email": "alice@example.com",
"score": 95.5
});
let result = validator.validate(&valid_user, &user_schema);
println!(
" Validation result: {}",
if result.valid { "PASSED" } else { "FAILED" }
);
println!(" Fields validated: {}", result.fields_validated);
println!(" Validation time: {:.2}ms", result.validation_time_ms);
println!(" ❌ Testing invalid user data...");
let invalid_user = json!({
"name": "", "age": -5, "email": "not-an-email", "score": 150.0 });
let result = validator.validate(&invalid_user, &user_schema);
println!(
" Validation result: {}",
if result.valid { "PASSED" } else { "FAILED" }
);
println!(" Number of errors: {}", result.errors.len());
for (i, error) in result.errors.iter().enumerate() {
println!(" Error {}: {} - {}", i + 1, error.path, error.message);
}
println!(" ⚠️ Testing data with missing required fields...");
let incomplete_user = json!({
"name": "Bob Smith"
});
let result = validator.validate(&incomplete_user, &user_schema);
println!(
" Validation result: {}",
if result.valid { "PASSED" } else { "FAILED" }
);
println!(" Number of errors: {}", result.errors.len());
Ok(())
}
#[allow(dead_code)]
fn demonstrate_nested_schema_validation() -> Result<(), Box<dyn std::error::Error>> {
println!("\n🏗️ Demonstrating Nested Schema Validation...");
let validator = SchemaValidator::new();
let dataset_schema = {
let mut properties = HashMap::new();
let mut metadata_props = HashMap::new();
metadata_props.insert("title".to_string(), schema_helpers::string().required());
metadata_props.insert(
"description".to_string(),
schema_helpers::string().optional(),
);
metadata_props.insert(
"created_date".to_string(),
schema_helpers::date().required(),
);
metadata_props.insert(
"version".to_string(),
schema_helpers::string()
.with_constraint(SchemaConstraint::Pattern(r"^\d+\.\d+\.\d+$".to_string()))
.required(),
);
properties.insert(
"metadata".to_string(),
schema_helpers::object(metadata_props).required(),
);
let measurement_schema = {
let mut measurement_props = HashMap::new();
measurement_props.insert(
"timestamp".to_string(),
schema_helpers::string()
.with_constraint(SchemaConstraint::Format("date-time".to_string()))
.required(),
);
measurement_props.insert("value".to_string(), schema_helpers::number().required());
measurement_props.insert(
"unit".to_string(),
schema_helpers::string()
.with_constraint(SchemaConstraint::Enum(vec![
json!("celsius"),
json!("fahrenheit"),
json!("kelvin"),
]))
.required(),
);
measurement_props.insert(
"quality".to_string(),
schema_helpers::string()
.with_constraint(SchemaConstraint::Enum(vec![
json!("good"),
json!("acceptable"),
json!("poor"),
]))
.optional(),
);
schema_helpers::object(measurement_props)
};
properties.insert(
"measurements".to_string(),
schema_helpers::array(measurement_schema)
.with_constraint(SchemaConstraint::MinLength(1))
.with_constraint(SchemaConstraint::MaxLength(10000))
.required(),
);
let mut stats_props = HashMap::new();
stats_props.insert(
"count".to_string(),
schema_helpers::positive_integer().required(),
);
stats_props.insert("mean".to_string(), schema_helpers::number().required());
stats_props.insert(
"std_dev".to_string(),
schema_helpers::non_negative_number().required(),
);
properties.insert(
"statistics".to_string(),
schema_helpers::object(stats_props).optional(),
);
schema_helpers::object(properties)
};
println!(" ✅ Testing valid nested dataset...");
let valid_dataset = json!({
"metadata": {
"title": "Temperature Measurements",
"description": "Daily temperature readings from weather station",
"created_date": "2024-01-15",
"version": "1.2.3"
},
"measurements": [
{
"timestamp": "2024-01-15T08:00:00Z",
"value": 20.5,
"unit": "celsius",
"quality": "good"
},
{
"timestamp": "2024-01-15T12:00:00Z",
"value": 25.2,
"unit": "celsius",
"quality": "good"
},
{
"timestamp": "2024-01-15T16:00:00Z",
"value": 22.8,
"unit": "celsius"
}
],
"statistics": {
"count": 3,
"mean": 22.83,
"std_dev": 2.35
}
});
let result = validator.validate(&valid_dataset, &dataset_schema);
println!(
" Validation result: {}",
if result.valid { "PASSED" } else { "FAILED" }
);
println!(" Fields validated: {}", result.fields_validated);
println!(" Validation time: {:.2}ms", result.validation_time_ms);
println!(" ❌ Testing invalid nested dataset...");
let invalid_dataset = json!({
"metadata": {
"title": "Temperature Measurements",
"created_date": "not-a-date", "version": "invalid-version" },
"measurements": [
{
"timestamp": "not-a-timestamp", "value": "not-a-number", "unit": "invalid-unit", "quality": "excellent" }
],
"statistics": {
"count": -1, "mean": "NaN", "std_dev": -5.0 }
});
let result = validator.validate(&invalid_dataset, &dataset_schema);
println!(
" Validation result: {}",
if result.valid { "PASSED" } else { "FAILED" }
);
println!(" Number of errors: {}", result.errors.len());
for (i, error) in result.errors.iter().take(5).enumerate() {
println!(" Error {}: {} - {}", i + 1, error.path, error.message);
}
if result.errors.len() > 5 {
println!(" ... and {} more errors", result.errors.len() - 5);
}
Ok(())
}
#[allow(dead_code)]
fn demonstrate_custom_validators() -> Result<(), Box<dyn std::error::Error>> {
println!("\n🔧 Demonstrating Custom Validators...");
let mut validator = SchemaValidator::new();
validator.add_custom_validator("is_even", |value| {
if let Some(num) = value.as_i64() {
num % 2 == 0
} else {
false
}
});
validator.add_custom_validator("is_positive_and_prime", |value| {
if let Some(num) = value.as_i64() {
if num <= 1 {
return false;
}
for i in 2..=((num as f64).sqrt() as i64) {
if num % i == 0 {
return false;
}
}
true
} else {
false
}
});
let custom_schema = {
let mut properties = HashMap::new();
properties.insert(
"even_number".to_string(),
schema_helpers::integer()
.with_constraint(SchemaConstraint::Custom("is_even".to_string()))
.required(),
);
properties.insert(
"prime_number".to_string(),
schema_helpers::integer()
.with_constraint(SchemaConstraint::Custom(
"is_positive_and_prime".to_string(),
))
.required(),
);
schema_helpers::object(properties)
};
println!(" ✅ Testing valid data with custom validators...");
let valid_data = json!({
"even_number": 42,
"prime_number": 17
});
let result = validator.validate(&valid_data, &custom_schema);
println!(
" Validation result: {}",
if result.valid { "PASSED" } else { "FAILED" }
);
println!(" ❌ Testing invalid data with custom validators...");
let invalid_data = json!({
"even_number": 43, "prime_number": 15 });
let result = validator.validate(&invalid_data, &custom_schema);
println!(
" Validation result: {}",
if result.valid { "PASSED" } else { "FAILED" }
);
for error in &result.errors {
println!(" Error: {} - {}", error.path, error.message);
}
Ok(())
}
#[allow(dead_code)]
fn demonstrate_format_validators() -> Result<(), Box<dyn std::error::Error>> {
println!("\n📧 Demonstrating Format Validators...");
let mut validator = SchemaValidator::new();
validator.add_format_validator("phone", |s| {
let phone_regex = regex::Regex::new(r"^\+?[\d\s\-\(\)]{10,15}$").expect("Operation failed");
phone_regex.is_match(s)
});
let contact_schema = {
let mut properties = HashMap::new();
properties.insert("email".to_string(), schema_helpers::email().required());
properties.insert(
"website".to_string(),
schema_helpers::string()
.with_constraint(SchemaConstraint::Format("uri".to_string()))
.optional(),
);
properties.insert(
"registration_date".to_string(),
schema_helpers::date().required(),
);
properties.insert(
"last_login".to_string(),
schema_helpers::string()
.with_constraint(SchemaConstraint::Format("date-time".to_string()))
.optional(),
);
properties.insert("user_id".to_string(), schema_helpers::uuid().required());
properties.insert(
"ip_address".to_string(),
schema_helpers::string()
.with_constraint(SchemaConstraint::Format("ipv4".to_string()))
.optional(),
);
properties.insert(
"phone".to_string(),
schema_helpers::string()
.with_constraint(SchemaConstraint::Format("phone".to_string()))
.optional(),
);
schema_helpers::object(properties)
};
println!(" ✅ Testing valid format data...");
let valid_contact = json!({
"email": "user@example.com",
"website": "https://example.com",
"registration_date": "2024-01-15",
"last_login": "2024-01-15T14:30:00Z",
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"ip_address": "192.168.1.1",
"phone": "+1 (555) 123-4567"
});
let result = validator.validate(&valid_contact, &contact_schema);
println!(
" Validation result: {}",
if result.valid { "PASSED" } else { "FAILED" }
);
println!(" ❌ Testing invalid format data...");
let invalid_contact = json!({
"email": "not-an-email",
"website": "not-a-url",
"registration_date": "2024-13-45", "last_login": "not-a-datetime",
"user_id": "not-a-uuid",
"ip_address": "999.999.999.999", "phone": "abc" });
let result = validator.validate(&invalid_contact, &contact_schema);
println!(
" Validation result: {}",
if result.valid { "PASSED" } else { "FAILED" }
);
println!(" Number of format errors: {}", result.errors.len());
for error in &result.errors {
println!(" Format error: {} - {}", error.path, error.message);
}
Ok(())
}
#[allow(dead_code)]
fn demonstrate_file_validation() -> Result<(), Box<dyn std::error::Error>> {
println!("\n📁 Demonstrating File Validation...");
let temp_dir = tempdir()?;
let validator = SchemaValidator::new();
let config_schema = {
let mut properties = HashMap::new();
properties.insert(
"app_name".to_string(),
schema_helpers::string()
.with_constraint(SchemaConstraint::MinLength(1))
.required(),
);
properties.insert(
"port".to_string(),
schema_helpers::integer()
.with_constraint(SchemaConstraint::MinValue(1.0))
.with_constraint(SchemaConstraint::MaxValue(65535.0))
.required(),
);
properties.insert("debug".to_string(), schema_helpers::boolean().required());
let mut db_props = HashMap::new();
db_props.insert("host".to_string(), schema_helpers::string().required());
db_props.insert(
"port".to_string(),
schema_helpers::positive_integer().required(),
);
db_props.insert("database".to_string(), schema_helpers::string().required());
properties.insert(
"database".to_string(),
schema_helpers::object(db_props).required(),
);
schema_helpers::object(properties)
};
let valid_config_path = temp_dir.path().join("valid_config.json");
let valid_config_content = json!({
"app_name": "MyApp",
"port": 8080,
"debug": true,
"database": {
"host": "localhost",
"port": 5432,
"database": "myapp_db"
}
});
std::fs::write(
&valid_config_path,
serde_json::to_string_pretty(&valid_config_content)?,
)?;
println!(" ✅ Validating valid configuration file...");
let result = validator.validate_file(&valid_config_path, &config_schema)?;
println!(
" Validation result: {}",
if result.valid { "PASSED" } else { "FAILED" }
);
println!(" Fields validated: {}", result.fields_validated);
let invalid_config_path = temp_dir.path().join("invalid_config.json");
let invalid_config_content = json!({
"app_name": "", "port": 70000, "debug": "yes", "database": {
"host": "localhost",
"port": -1 }
});
std::fs::write(
&invalid_config_path,
serde_json::to_string_pretty(&invalid_config_content)?,
)?;
println!(" ❌ Validating invalid configuration file...");
let result = validator.validate_file(&invalid_config_path, &config_schema)?;
println!(
" Validation result: {}",
if result.valid { "PASSED" } else { "FAILED" }
);
println!(" Number of errors: {}", result.errors.len());
for error in &result.errors {
println!(" Error: {} - {}", error.path, error.message);
}
Ok(())
}
#[allow(dead_code)]
fn demonstrate_json_schema_compatibility() -> Result<(), Box<dyn std::error::Error>> {
println!("\n🔄 Demonstrating JSON Schema Compatibility...");
let validator = SchemaValidator::new();
let json_schema = json!({
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 50
},
"age": {
"type": "integer",
"minimum": 0,
"maximum": 120
},
"email": {
"type": "string",
"format": "email"
},
"scores": {
"type": "array",
"items": {
"type": "number",
"minimum": 0,
"maximum": 100
}
}
}
});
let schema = scirs2_io::validation::schema_from_json_schema(&json_schema)?;
println!(" 🔄 Testing JSON Schema compatibility...");
let valid_data = json!({
"name": "Jane Doe",
"age": 28,
"email": "jane@example.com",
"scores": [85.5, 92.0, 78.5, 95.0]
});
let result = validator.validate(&valid_data, &schema);
println!(
" Valid data result: {}",
if result.valid { "PASSED" } else { "FAILED" }
);
let invalid_data = json!({
"name": "", "age": 150, "email": "not-an-email",
"scores": [85.5, 105.0, -10.0] });
let result = validator.validate(&invalid_data, &schema);
println!(
" Invalid data result: {}",
if result.valid { "PASSED" } else { "FAILED" }
);
println!(" Errors found: {}", result.errors.len());
println!(" ✅ JSON Schema conversion and validation successful!");
Ok(())
}