# JSON Schema Validation Guide
## Overview
The Mecha10 framework provides comprehensive JSON Schema validation for configuration files. This catches configuration errors early with helpful, detailed error messages.
## Why Schema Validation?
**Without validation:**
```
Error: Node 'camera' failed to start
-> Reason: Unknown
-> Spent 20 minutes debugging
```
**With schema validation:**
```
Configuration validation failed:
- /nodes/drivers/0/run_target: references undefined run_target 'robott'
(Did you mean 'robot'? Available targets: robot, remote, dev)
- /nodes/custom/1/name: Duplicate node name 'planner'
(Node names must be unique across all categories)
```
## Features
### 1. JSON Schema v7 Validation
- **Type checking**: Ensures fields have correct types (string, number, boolean, array, object)
- **Format validation**: Validates patterns (e.g., project names, versions, topic names)
- **Required fields**: Ensures critical fields are present
- **Range constraints**: Validates numeric ranges and string lengths
### 2. Custom Validation Rules
- **Circular dependency detection**: Prevents infinite dependency loops between nodes
- **Reference validation**: Ensures run_target references exist
- **Uniqueness checks**: Validates node names are unique
- **Resource specification validation**: Validates memory and CPU formats
### 3. Detailed Error Messages
- **Field paths**: Shows exactly where the error is (e.g., `/nodes/drivers/0/name`)
- **Context**: Displays the invalid value
- **Suggestions**: Provides helpful hints when possible
## Usage
### Basic Validation
```rust
use mecha10::prelude::*;
// Validate a project configuration file
validate_project_config("mecha10.json")?;
// Validate JSON value directly
let config = serde_json::json!({
"name": "my-robot",
"version": "1.0.0"
});
validate_project_json(&config)?;
```
### Load and Validate
```rust
// Load and validate in one step
let config = load_and_validate_project("mecha10.json")?;
// Now safe to use
println!("Project: {}", config["name"]);
```
### Node Configuration Validation
```rust
// Validate NodeConfig
let node_config = serde_json::json!({
"id": "camera_node",
"executable": "./camera_node",
"mode": "local"
});
validate_node_config_json(&node_config)?;
```
### Export Schema for IDE Integration
```rust
// Export schema to file
export_project_schema("project.schema.json")?;
```
Then reference in your `mecha10.json`:
```json
{
"$schema": "./project.schema.json",
"name": "my-robot",
...
}
```
IDEs like VS Code will provide:
- Auto-completion for fields
- Inline documentation
- Real-time validation as you type
- Error highlighting
## Configuration Structure
### Project Configuration (mecha10.json)
```json
{
"$schema": "https://mecha10.dev/schemas/project.schema.json",
"name": "my-robot", // Required: lowercase, alphanumeric, hyphens
"version": "1.0.0", // Required: semver (X.Y.Z or X.Y.Z-suffix)
"description": "...", // Optional: max 500 chars
"author": "...", // Optional: max 100 chars
"license": "MIT", // Optional: max 50 chars
"robot": { // Optional
"id": "${ROBOT_ID}", // Can use env vars
"type": "service", // Enum: service, delivery, industrial, mobile, manipulator, custom
"platform": "differential-drive" // String
},
"nodes": {
"drivers": [...], // Hardware driver nodes
"standard": [...], // Pre-built nodes from packages
"custom": [...] // Custom application nodes
},
"run_targets": {
"robot": {...}, // Execution target definitions
"remote": {...}
},
"behaviors": { // Optional
"default": "./behaviors/auto.xml"
},
"simulation": { // Optional
"enabled": true,
"environment": "./env.tscn"
},
"scripts": { // Optional: npm scripts-like
"dev": "cargo run",
"build": "cargo build --release"
}
}
```
### Node Definition
```json
{
"name": "camera_fake", // Required: snake_case
"path": "./drivers/camera_fake", // Required: source path
"config": "./config/camera.yaml", // Optional: config file path
"description": "...", // Optional: max 200 chars
"run_target": "robot", // Optional: where to run (must exist in run_targets)
"enabled": true, // Optional: default true
"auto_restart": true, // Optional: default true
"depends_on": ["lidar", "imu"] // Optional: node dependencies
}
```
### Run Target Definition
```json
{
"robot": {
"description": "On-robot execution",
"includes": ["robot", "shared"],
"resources": {
"cpu_limit": "2.0", // String: number of cores
"memory_limit": "512M", // String: <number><K|M|G|T>
"gpu": false, // Boolean or object
"gpu": { // Detailed GPU spec
"enabled": true,
"vendor": "nvidia", // Enum: nvidia, amd, intel
"count": 1, // Integer: number of GPUs
"optional": false // Boolean
},
"storage_type": "local", // Enum: local, ssd, hdd, any
"storage_size_gb": 100, // Integer: GB
"model_cache": "/mnt/models", // String: path
"devices": [ // Array of device paths
"/dev/video0",
"/dev/ttyUSB0"
]
}
}
}
```
## Validation Rules
### Project Name
- **Pattern**: `^[a-z0-9][a-z0-9-]*$`
- **Length**: 1-64 characters
- **Valid**: `my-robot`, `robot-v2`, `delivery-bot`
- **Invalid**: `My Robot` (uppercase), `robot_bot` (underscore), `123robot` (starts with number)
### Version
- **Pattern**: `^\d+\.\d+\.\d+(-.+)?$`
- **Valid**: `1.0.0`, `2.1.5-beta`, `0.0.1-alpha.1`
- **Invalid**: `1.0` (missing patch), `v1.0.0` (prefix), `1.0.0.0` (too many parts)
### Node Name
- **Pattern**: `^[a-z][a-z0-9_]*$`
- **Valid**: `camera_fake`, `lidar2`, `motor_controller`
- **Invalid**: `Camera` (uppercase), `_camera` (leading underscore), `camera-fake` (hyphen)
### Topic Name
- **Pattern**: `^/[a-z0-9/_-]+$`
- **Valid**: `/sensor/camera/rgb`, `/actuator/motor/cmd_vel`
- **Invalid**: `sensor/camera` (missing leading slash), `/Sensor/Camera` (uppercase)
### Environment Variable Name
- **Pattern**: `^[A-Z_][A-Z0-9_]*$`
- **Valid**: `ROBOT_ID`, `API_KEY`, `_INTERNAL`
- **Invalid**: `robot_id` (lowercase), `1ST_VAR` (starts with number)
### Memory Limit
- **Pattern**: `^\d+(K|M|G|T)?$`
- **Valid**: `512M`, `16G`, `1024K`, `2T`, `100` (bytes)
- **Invalid**: `512 M` (space), `16GB` (multiple letters), `1.5G` (decimal)
### CPU Limit
- **Pattern**: `^\d+(\.\d+)?$`
- **Valid**: `2.0`, `4`, `0.5`, `16.0`
- **Invalid**: `2 cores`, `4.` (trailing dot), `.5` (leading dot)
## Custom Validation Rules
### 1. No Circular Dependencies
**Invalid:**
```json
{
"nodes": {
"drivers": [
{ "name": "camera", "depends_on": ["lidar"] },
{ "name": "lidar", "depends_on": ["imu"] },
{ "name": "imu", "depends_on": ["camera"] } // ❌ Circular!
]
}
}
```
**Error:**
```
Circular dependency detected involving node 'camera'
```
### 2. Run Target References Must Exist
**Invalid:**
```json
{
"nodes": {
"drivers": [
{ "name": "camera", "run_target": "robott" } // ❌ Typo!
]
},
"run_targets": {
"robot": {...},
"remote": {...}
}
}
```
**Error:**
```
Node 'camera' references undefined run_target 'robott'.
Available targets: robot, remote
```
### 3. Node Names Must Be Unique
**Invalid:**
```json
{
"nodes": {
"drivers": [
{ "name": "camera", "path": "./drivers/camera" }
],
"custom": [
{ "name": "camera", "path": "./custom/camera" } // ❌ Duplicate!
]
}
}
```
**Error:**
```
Duplicate node name 'camera' found.
Node names must be unique across all categories.
```
### 4. Resource Specifications
**Invalid:**
```json
{
"run_targets": {
"robot": {
"resources": {
"memory_limit": "512 MB", // ❌ Space not allowed
"cpu_limit": "two cores" // ❌ Must be numeric
}
}
}
}
```
**Error:**
```
Invalid memory_limit '512 MB' in run_target 'robot'.
Invalid cpu_limit 'two cores' in run_target 'robot'.
Expected a number (e.g., '2.0')
```
## Integration Examples
### Example 1: Validate on Startup
```rust
use mecha10::prelude::*;
#[tokio::main]
async fn main() -> Result<()> {
// Validate config before doing anything else
validate_project_config("mecha10.json")
.context("Configuration validation failed")?;
// Load nodes, etc.
let config = load_and_validate_project("mecha10.json")?;
// Continue with startup...
Ok(())
}
```
### Example 2: CLI Validation Command
```bash
# Validate project configuration
mecha10 validate mecha10.json
# Validate node configuration
mecha10 validate-node config/camera.json
# Export schema
mecha10 schema export project.schema.json
```
### Example 3: CI/CD Integration
```yaml
# .github/workflows/validate.yml
name: Validate Configuration
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Validate mecha10.json
run: cargo run --bin mecha10 -- validate mecha10.json
```
### Example 4: Pre-commit Hook
```bash
#!/bin/bash
# .git/hooks/pre-commit
# Validate configuration before commit
if ! cargo run --bin mecha10 -- validate mecha10.json; then
echo "❌ Configuration validation failed!"
echo "Fix errors before committing."
exit 1
fi
echo "✅ Configuration validation passed"
```
## Error Message Format
Validation errors follow this format:
```
Configuration validation failed:
- <field_path>: <error_message> (<current_value>)
```
### Examples:
**Missing required field:**
```
Configuration validation failed:
- root: 'name' is a required property
```
**Invalid format:**
```
Configuration validation failed:
- /name: "My Robot" does not match pattern "^[a-z0-9][a-z0-9-]*$"
```
**Type mismatch:**
```
Configuration validation failed:
- /nodes/drivers/0/enabled: "yes" is not of type 'boolean'
```
**Enum validation:**
```
Configuration validation failed:
- /robot/type: "custom-robot" is not one of ["service", "delivery", "industrial", "mobile", "manipulator", "custom"]
```
## Best Practices
### 1. Validate Early
Always validate configuration before starting any nodes:
```rust
// ✅ Good
validate_project_config("mecha10.json")?;
let runtime = Runtime::new(&config)?;
// ❌ Bad
let runtime = Runtime::new(&config)?; // May fail deep in startup
```
### 2. Use Schema References
Add `$schema` to your config files for IDE support:
```json
{
"$schema": "https://mecha10.dev/schemas/project.schema.json",
"name": "my-robot",
...
}
```
### 3. Automate Validation
- Run validation in CI/CD pipelines
- Add pre-commit hooks
- Validate in development mode on file changes
### 4. Export Schemas for Team
Share the schema with your team:
```bash
# Export schema
mecha10 schema export schemas/project.schema.json
# Commit to repo
git add schemas/project.schema.json
git commit -m "Add JSON schema for validation"
```
### 5. Handle Errors Gracefully
```rust
match validate_project_config("mecha10.json") {
Ok(_) => {
info!("✅ Configuration validated");
// Continue
}
Err(e) => {
error!("❌ Configuration validation failed:");
error!("{}", e);
error!("");
error!("Please fix the errors above and try again.");
std::process::exit(1);
}
}
```
## Advanced Usage
### Custom Validation Rules
Extend validation with your own rules:
```rust
use mecha10::schema_validation::*;
pub fn validate_with_custom_rules(config: &Value) -> Result<()> {
// Standard validation
validate_project_json(config)?;
// Custom rule: ensure at least one driver
if let Some(nodes) = config.get("nodes") {
if let Some(drivers) = nodes.get("drivers").and_then(|d| d.as_array()) {
if drivers.is_empty() {
return Err(Mecha10Error::Configuration(
"Project must have at least one driver node".to_string()
));
}
}
}
// Custom rule: warn about development targets in production
if let Some(targets) = config.get("run_targets") {
if targets.get("dev").is_some() {
warn!("Development run_target found in production config");
}
}
Ok(())
}
```
### Validation in Tests
```rust
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_config_validity() {
// Ensure test configs are valid
validate_project_config("tests/fixtures/valid_config.json")
.expect("Test config should be valid");
}
#[test]
fn test_invalid_config_detection() {
// Ensure invalid configs are caught
let result = validate_project_config("tests/fixtures/invalid_config.json");
assert!(result.is_err());
}
}
```
## Troubleshooting
### Common Errors
**1. "Pattern does not match"**
- Check the field format against the pattern
- Common issues: uppercase in names, wrong separators
**2. "Is not of type 'X'"**
- Ensure the value has the correct type
- Common issues: strings for booleans (`"true"` vs `true`)
**3. "Is not one of [...]"**
- Value must be one of the enumerated options
- Check for typos
**4. "Circular dependency detected"**
- Review `depends_on` fields
- Draw a dependency graph to visualize
**5. "References undefined run_target"**
- Ensure all `run_target` values exist in `run_targets`
- Check for typos
### Debugging Tips
1. **Use a JSON validator first**: Ensure basic JSON syntax is correct
2. **Check field paths**: Error messages show exact location
3. **Read the pattern**: Understand what format is expected
4. **Start minimal**: Begin with minimal valid config, add incrementally
5. **Export the schema**: Review full schema definition for clarity
## Related Documentation
- [Configuration Loading Guide](./CONFIG_GUIDE.md)
- [Node Development Guide](./NODE_GUIDE.md)
- [Project Structure](./PROJECT_STRUCTURE.md)
- [JSON Schema Specification](https://json-schema.org/specification.html)
## Summary
Schema validation provides:
- ✅ **Early error detection** - Catch issues before runtime
- ✅ **Better error messages** - Know exactly what's wrong and where
- ✅ **IDE integration** - Auto-complete and inline validation
- ✅ **Documentation** - Schema serves as spec for valid configs
- ✅ **Consistency** - Enforce standards across projects
- ✅ **Confidence** - Deploy knowing configs are valid
Use it everywhere: development, testing, CI/CD, and production!