| Crates.io | rson-schema |
| lib.rs | rson-schema |
| version | 1.0.0 |
| created_at | 2025-05-31 21:15:03.780185+00 |
| updated_at | 2025-05-31 21:25:05.000423+00 |
| description | Schema validation for RSON |
| homepage | https://rson.dev |
| repository | https://github.com/RSON-Rust-Serialized-Object-Notation/RSON-core |
| max_upload_size | |
| id | 1696652 |
| size | 16,518 |
Schema validation and type safety for RSON (Rust Serialized Object Notation)
rson-schema provides powerful schema validation for RSON data, ensuring your configuration files and data structures conform to expected formats and constraints.
Key Features:
[dependencies]
rson-schema = "0.1.0"
serde = { version = "1.0", features = ["derive"] }
use rson_schema::{Schema, validate, SchemaBuilder};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
struct Config {
#[schema(min_length = 1, max_length = 50)]
name: String,
#[schema(minimum = 1, maximum = 65535)]
port: u16,
#[schema(pattern = "^[a-z0-9]+$")]
database_name: String,
features: Vec<String>,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Generate schema from Rust type
let schema = Schema::from_type::<Config>()?;
// Validate RSON data against schema
let rson_text = r#"
Config(
name: "my-app",
port: 8080,
database_name: "mydb123",
features: ["auth", "logging"],
)
"#;
// Validate returns detailed errors if validation fails
match validate(&schema, rson_text) {
Ok(_) => println!("โ
Valid RSON data"),
Err(errors) => {
for error in errors {
println!("โ {}", error);
}
}
}
Ok(())
}
Add validation constraints directly to your Rust types:
use serde::{Deserialize, Serialize};
use rson_schema::schema;
#[derive(Serialize, Deserialize)]
struct User {
#[schema(min_length = 2, max_length = 50)]
name: String,
#[schema(minimum = 13, maximum = 120)]
age: u8,
#[schema(pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")]
email: String,
#[schema(items = "String", min_items = 1, max_items = 10)]
roles: Vec<String>,
#[schema(optional)]
phone: Option<String>,
}
use rson_schema::{SchemaBuilder, SchemaType, Constraint};
let schema = SchemaBuilder::new()
.struct_type("Config")
.field("name", SchemaType::String)
.constraint(Constraint::MinLength(1))
.constraint(Constraint::MaxLength(100))
.field("port", SchemaType::Integer)
.constraint(Constraint::Minimum(1))
.constraint(Constraint::Maximum(65535))
.field("enabled", SchemaType::Boolean)
.field("tags", SchemaType::Array(Box::new(SchemaType::String)))
.constraint(Constraint::MinItems(0))
.constraint(Constraint::MaxItems(20))
.build()?;
#[derive(Serialize, Deserialize)]
struct StringValidation {
#[schema(min_length = 5, max_length = 50)]
name: String,
#[schema(pattern = r"^[A-Z][a-z]+$")]
title: String,
#[schema(format = "email")]
email: String,
#[schema(format = "uri")]
website: String,
#[schema(enum_values = ["dev", "staging", "prod"])]
environment: String,
}
#[derive(Serialize, Deserialize)]
struct NumericValidation {
#[schema(minimum = 0, maximum = 100)]
percentage: u8,
#[schema(exclusive_minimum = 0.0)]
price: f64,
#[schema(multiple_of = 5)]
step: u32,
}
#[derive(Serialize, Deserialize)]
struct ArrayValidation {
#[schema(min_items = 1, max_items = 10)]
tags: Vec<String>,
#[schema(unique_items = true)]
ids: Vec<u32>,
#[schema(items = "Integer", min_value = 1, max_value = 100)]
scores: Vec<u8>,
}
#[derive(Serialize, Deserialize)]
struct ObjectValidation {
#[schema(min_properties = 1, max_properties = 50)]
metadata: std::collections::HashMap<String, String>,
#[schema(required = ["host", "port"])]
config: DatabaseConfig,
}
// config.rschema
Schema(
type: "struct",
name: "Config",
fields: {
"name": FieldSchema(
type: "string",
constraints: [
MinLength(1),
MaxLength(50),
],
),
"port": FieldSchema(
type: "integer",
constraints: [
Minimum(1),
Maximum(65535),
],
),
"features": FieldSchema(
type: "array",
items: "string",
constraints: [
MinItems(0),
MaxItems(20),
],
),
},
)
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 50
},
"port": {
"type": "integer",
"minimum": 1,
"maximum": 65535
},
"features": {
"type": "array",
"items": { "type": "string" },
"minItems": 0,
"maxItems": 20
}
},
"required": ["name", "port", "features"]
}
use rson_schema::{Validator, ValidationResult, ValidationError};
struct EmailValidator;
impl Validator for EmailValidator {
fn validate(&self, value: &str) -> ValidationResult {
if value.contains('@') && value.contains('.') {
Ok(())
} else {
Err(ValidationError::new("Invalid email format"))
}
}
}
// Use custom validator
#[derive(Serialize, Deserialize)]
struct User {
#[schema(custom = EmailValidator)]
email: String,
}
#[derive(Serialize, Deserialize)]
struct ConditionalConfig {
#[schema(required_if = "ssl_enabled == true")]
ssl_cert: Option<String>,
ssl_enabled: bool,
#[schema(min_value_if = "environment == 'prod'", min_value = 8080)]
port: u16,
environment: String,
}
use rson_schema::cross_field_validator;
#[derive(Serialize, Deserialize)]
#[schema(cross_field = "validate_password_confirmation")]
struct RegisterUser {
password: String,
password_confirmation: String,
}
#[cross_field_validator]
fn validate_password_confirmation(data: &RegisterUser) -> ValidationResult {
if data.password == data.password_confirmation {
Ok(())
} else {
Err(ValidationError::new("Passwords do not match"))
}
}
# Generate schema from Rust code
rsonc schema generate --type Config --output config.rschema src/config.rs
# Generate JSON Schema
rsonc schema generate --type Config --format json-schema --output config.schema.json src/config.rs
# Validate RSON file against schema
rsonc validate --schema config.rschema config.rson
# Validate multiple files
rsonc validate --schema config.rschema configs/*.rson
Schema validation is designed to be fast:
| Operation | Speed | Memory Usage |
|---|---|---|
| Schema compile | ~1ms | Low |
| Validate small file | ~100ฮผs | Minimal |
| Validate large file | ~10MB/s | Low |
Benchmarks on typical configuration files
// Validate server configuration
#[derive(Serialize, Deserialize)]
struct ServerConfig {
#[schema(format = "ipv4")]
host: String,
#[schema(minimum = 1024, maximum = 65535)]
port: u16,
#[schema(minimum = 1, maximum = 100)]
worker_threads: u8,
#[schema(enum_values = ["debug", "info", "warn", "error"])]
log_level: String,
}
// Validate incoming API requests
#[derive(Serialize, Deserialize)]
struct CreateUserRequest {
#[schema(min_length = 2, max_length = 50, pattern = r"^[a-zA-Z\s]+$")]
name: String,
#[schema(format = "email")]
email: String,
#[schema(minimum = 13, maximum = 120)]
age: u8,
#[schema(items = "String", enum_values = ["user", "admin", "moderator"])]
roles: Vec<String>,
}
// Validate data transformation outputs
#[derive(Serialize, Deserialize)]
struct ProcessedData {
#[schema(format = "iso8601")]
timestamp: String,
#[schema(minimum = 0.0)]
value: f64,
#[schema(pattern = r"^[A-Z]{2,4}$")]
currency_code: String,
#[schema(min_properties = 1)]
metadata: std::collections::HashMap<String, serde_json::Value>,
}
#[cfg(test)]
mod tests {
use super::*;
use rson_schema::{validate, Schema};
#[test]
fn test_valid_config() {
let schema = Schema::from_type::<Config>().unwrap();
let rson = r#"Config(name: "test", port: 8080, features: ["auth"])"#;
assert!(validate(&schema, rson).is_ok());
}
#[test]
fn test_invalid_port() {
let schema = Schema::from_type::<Config>().unwrap();
let rson = r#"Config(name: "test", port: 70000, features: ["auth"])"#;
let result = validate(&schema, rson);
assert!(result.is_err());
let errors = result.unwrap_err();
assert!(errors.iter().any(|e| e.field == "port"));
}
}
We welcome contributions! Please see our Contributing Guide.
Areas we need help with:
This project is licensed under the MIT License - see the LICENSE file for details.
Made with ๐ก๏ธ by the RSON community