🦀 📄 @json_file Function in Rust

Rust Documentation

📄 @json_file Function in Rust

The @json_file function in TuskLang provides specialized JSON file operations in Rust, with schema validation, type safety, and high-performance JSON processing capabilities.

Basic Usage

// Basic JSON file operations
let data = @json_file.read("config.json")?;
@json_file.write("data.json", @json_data)?;
let exists = @json_file.exists("settings.json")?;

// JSON file operations with validation let validated_data = @json_file.read_validate("config.json", @schema)?; @json_file.write_pretty("output.json", @formatted_data)?; let parsed_data = @json_file.parse("data.json")?;

JSON File Operations

// Comprehensive JSON file operations
struct JsonFileManager {
    base_path: std::path::PathBuf,
    schema_registry: std::collections::HashMap<String, serde_json::Value>,
    cache: std::collections::HashMap<String, (serde_json::Value, std::time::Instant)>,
}

impl JsonFileManager { fn new(base_path: &str) -> Result<Self, Box<dyn std::error::Error>> { let base_path = std::path::PathBuf::from(base_path); if !base_path.exists() { std::fs::create_dir_all(&base_path)?; } Ok(Self { base_path, schema_registry: std::collections::HashMap::new(), cache: std::collections::HashMap::new(), }) } // Read JSON file with caching fn read_json(&mut self, path: &str) -> Result<serde_json::Value, Box<dyn std::error::Error>> { let full_path = self.base_path.join(path); // Validate path self.validate_path(&full_path)?; // Check cache first let cache_key = full_path.to_string_lossy().to_string(); if let Some((cached_data, timestamp)) = self.cache.get(&cache_key) { if timestamp.elapsed() < std::time::Duration::from_secs(300) { // 5 minutes cache return Ok(cached_data.clone()); } } // Read and parse JSON let content = std::fs::read_to_string(&full_path)?; let json_data = serde_json::from_str(&content)?; // Cache result self.cache.insert(cache_key, (json_data.clone(), std::time::Instant::now())); Ok(json_data) } // Write JSON file with atomic operation fn write_json(&self, path: &str, data: &serde_json::Value) -> Result<(), Box<dyn std::error::Error>> { let full_path = self.base_path.join(path); // Validate path self.validate_path(&full_path)?; // Ensure directory exists if let Some(parent) = full_path.parent() { std::fs::create_dir_all(parent)?; } // Write atomically using temporary file let temp_path = full_path.with_extension("tmp"); let json_string = serde_json::to_string_pretty(data)?; std::fs::write(&temp_path, json_string)?; std::fs::rename(&temp_path, &full_path)?; Ok(()) } // Read JSON with schema validation fn read_json_validate(&mut self, path: &str, schema: &serde_json::Value) -> Result<serde_json::Value, Box<dyn std::error::Error>> { let json_data = self.read_json(path)?; // Validate against schema self.validate_against_schema(&json_data, schema)?; Ok(json_data) } // Write JSON with pretty formatting fn write_json_pretty(&self, path: &str, data: &serde_json::Value) -> Result<(), Box<dyn std::error::Error>> { let full_path = self.base_path.join(path); // Validate path self.validate_path(&full_path)?; // Ensure directory exists if let Some(parent) = full_path.parent() { std::fs::create_dir_all(parent)?; } // Write with pretty formatting let json_string = serde_json::to_string_pretty(data)?; std::fs::write(&full_path, json_string)?; Ok(()) } // Parse JSON string fn parse_json(&self, content: &str) -> Result<serde_json::Value, Box<dyn std::error::Error>> { let json_data = serde_json::from_str(content)?; Ok(json_data) } // Validate path security fn validate_path(&self, path: &std::path::Path) -> Result<(), Box<dyn std::error::Error>> { // Check for path traversal attempts if path.to_string_lossy().contains("..") { return Err("Path traversal attempt detected".into()); } // Ensure path is within base directory if !path.starts_with(&self.base_path) { return Err("Path outside allowed directory".into()); } // Check file extension if let Some(extension) = path.extension() { if extension.to_string_lossy().to_lowercase() != "json" { return Err("File must have .json extension".into()); } } Ok(()) } // Validate JSON against schema fn validate_against_schema(&self, data: &serde_json::Value, schema: &serde_json::Value) -> Result<(), Box<dyn std::error::Error>> { // Use jsonschema crate for validation let compiled_schema = jsonschema::JSONSchema::compile(schema)?; if let Err(errors) = compiled_schema.validate(data) { let error_messages: Vec<String> = errors.map(|e| e.to_string()).collect(); return Err(format!("JSON validation failed: {}", error_messages.join(", ")).into()); } Ok(()) } }

JSON Schema Management

// JSON schema management
struct JsonSchemaManager {
    schemas: std::collections::HashMap<String, serde_json::Value>,
    validators: std::collections::HashMap<String, jsonschema::JSONSchema>,
}

impl JsonSchemaManager { fn new() -> Self { Self { schemas: std::collections::HashMap::new(), validators: std::collections::HashMap::new(), } } // Register schema fn register_schema(&mut self, name: &str, schema: serde_json::Value) -> Result<(), Box<dyn std::error::Error>> { // Compile schema for validation let compiled_schema = jsonschema::JSONSchema::compile(&schema)?; self.schemas.insert(name.to_string(), schema); self.validators.insert(name.to_string(), compiled_schema); Ok(()) } // Get schema fn get_schema(&self, name: &str) -> Option<&serde_json::Value> { self.schemas.get(name) } // Validate data against schema fn validate_data(&self, name: &str, data: &serde_json::Value) -> Result<(), Box<dyn std::error::Error>> { if let Some(validator) = self.validators.get(name) { if let Err(errors) = validator.validate(data) { let error_messages: Vec<String> = errors.map(|e| e.to_string()).collect(); return Err(format!("Schema validation failed: {}", error_messages.join(", ")).into()); } } else { return Err(format!("Schema '{}' not found", name).into()); } Ok(()) } // Load schema from file fn load_schema_from_file(&mut self, name: &str, path: &str) -> Result<(), Box<dyn std::error::Error>> { let schema_content = std::fs::read_to_string(path)?; let schema = serde_json::from_str(&schema_content)?; self.register_schema(name, schema) } }

// Common JSON schemas fn get_common_schemas() -> std::collections::HashMap<String, serde_json::Value> { let mut schemas = std::collections::HashMap::new(); // User schema let user_schema = serde_json::json!({ "type": "object", "properties": { "id": { "type": "integer" }, "name": { "type": "string", "minLength": 1 }, "email": { "type": "string", "format": "email" }, "age": { "type": "integer", "minimum": 0, "maximum": 150 } }, "required": ["id", "name", "email"] }); schemas.insert("user".to_string(), user_schema); // Configuration schema let config_schema = serde_json::json!({ "type": "object", "properties": { "app_name": { "type": "string" }, "version": { "type": "string" }, "debug": { "type": "boolean" }, "port": { "type": "integer", "minimum": 1, "maximum": 65535 }, "database": { "type": "object", "properties": { "url": { "type": "string" }, "max_connections": { "type": "integer", "minimum": 1 } }, "required": ["url"] } }, "required": ["app_name", "version", "port"] }); schemas.insert("config".to_string(), config_schema); schemas }

Type-Safe JSON Operations

// Type-safe JSON operations
struct TypedJsonManager {
    type_registry: std::collections::HashMap<String, Box<dyn JsonTypeHandler>>,
}

trait JsonTypeHandler { fn validate(&self, data: &serde_json::Value) -> Result<(), Box<dyn std::error::Error>>; fn convert(&self, data: &serde_json::Value) -> Result<Value, Box<dyn std::error::Error>>; }

impl TypedJsonManager { fn new() -> Self { let mut manager = Self { type_registry: std::collections::HashMap::new(), }; // Register type handlers manager.register_type_handler("User", Box::new(UserTypeHandler)); manager.register_type_handler("Config", Box::new(ConfigTypeHandler)); manager.register_type_handler("LogEntry", Box::new(LogEntryTypeHandler)); manager } fn register_type_handler(&mut self, type_name: &str, handler: Box<dyn JsonTypeHandler>) { self.type_registry.insert(type_name.to_string(), handler); } fn read_typed<T>(&self, path: &str) -> Result<T, Box<dyn std::error::Error>> where T: serde::de::DeserializeOwned, { let content = std::fs::read_to_string(path)?; let data: T = serde_json::from_str(&content)?; Ok(data) } fn write_typed<T>(&self, path: &str, data: &T) -> Result<(), Box<dyn std::error::Error>> where T: serde::Serialize, { let json_string = serde_json::to_string_pretty(data)?; std::fs::write(path, json_string)?; Ok(()) } }

// User type handler struct UserTypeHandler;

impl JsonTypeHandler for UserTypeHandler { fn validate(&self, data: &serde_json::Value) -> Result<(), Box<dyn std::error::Error>> { if let Some(obj) = data.as_object() { if !obj.contains_key("id") || !obj.contains_key("name") || !obj.contains_key("email") { return Err("User must have id, name, and email fields".into()); } if let Some(email) = obj.get("email").and_then(|e| e.as_str()) { if !email.contains('@') { return Err("Invalid email format".into()); } } } else { return Err("User data must be an object".into()); } Ok(()) } fn convert(&self, data: &serde_json::Value) -> Result<Value, Box<dyn std::error::Error>> { // Convert to TuskLang Value Ok(serde_json::from_value(data.clone())?) } }

// Configuration type handler struct ConfigTypeHandler;

impl JsonTypeHandler for ConfigTypeHandler { fn validate(&self, data: &serde_json::Value) -> Result<(), Box<dyn std::error::Error>> { if let Some(obj) = data.as_object() { if !obj.contains_key("app_name") || !obj.contains_key("version") || !obj.contains_key("port") { return Err("Config must have app_name, version, and port fields".into()); } if let Some(port) = obj.get("port").and_then(|p| p.as_u64()) { if port == 0 || port > 65535 { return Err("Port must be between 1 and 65535".into()); } } } else { return Err("Config data must be an object".into()); } Ok(()) } fn convert(&self, data: &serde_json::Value) -> Result<Value, Box<dyn std::error::Error>> { Ok(serde_json::from_value(data.clone())?) } }

// Log entry type handler struct LogEntryTypeHandler;

impl JsonTypeHandler for LogEntryTypeHandler { fn validate(&self, data: &serde_json::Value) -> Result<(), Box<dyn std::error::Error>> { if let Some(obj) = data.as_object() { if !obj.contains_key("timestamp") || !obj.contains_key("level") || !obj.contains_key("message") { return Err("Log entry must have timestamp, level, and message fields".into()); } if let Some(level) = obj.get("level").and_then(|l| l.as_str()) { let valid_levels = ["debug", "info", "warn", "error"]; if !valid_levels.contains(&level) { return Err("Invalid log level".into()); } } } else { return Err("Log entry data must be an object".into()); } Ok(()) } fn convert(&self, data: &serde_json::Value) -> Result<Value, Box<dyn std::error::Error>> { Ok(serde_json::from_value(data.clone())?) } }

JSON Data Structures

// JSON data structures with serde
#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct User {
    id: u32,
    name: String,
    email: String,
    age: Option<u32>,
    preferences: Option<UserPreferences>,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)] struct UserPreferences { theme: String, notifications: bool, language: String, }

#[derive(Debug, serde::Serialize, serde::Deserialize)] struct AppConfig { app_name: String, version: String, debug: bool, port: u16, database: DatabaseConfig, features: Vec<String>, }

#[derive(Debug, serde::Serialize, serde::Deserialize)] struct DatabaseConfig { url: String, max_connections: u32, timeout: u64, }

#[derive(Debug, serde::Serialize, serde::Deserialize)] struct LogEntry { timestamp: chrono::DateTime<chrono::Utc>, level: LogLevel, message: String, metadata: std::collections::HashMap<String, serde_json::Value>, }

#[derive(Debug, serde::Serialize, serde::Deserialize)] enum LogLevel { Debug, Info, Warn, Error, }

// JSON operations with typed structures fn typed_json_operations() -> Result<(), Box<dyn std::error::Error>> { let typed_manager = TypedJsonManager::new(); // Read typed configuration let config: AppConfig = typed_manager.read_typed("config.json")?; println!("App: {} v{}", config.app_name, config.version); // Write typed user data let user = User { id: 1, name: "John Doe".to_string(), email: "john@example.com".to_string(), age: Some(30), preferences: Some(UserPreferences { theme: "dark".to_string(), notifications: true, language: "en".to_string(), }), }; typed_manager.write_typed("user.json", &user)?; // Read and write log entries let log_entry = LogEntry { timestamp: chrono::Utc::now(), level: LogLevel::Info, message: "Application started".to_string(), metadata: { let mut meta = std::collections::HashMap::new(); meta.insert("user_id".to_string(), serde_json::Value::Number(1.into())); meta }, }; typed_manager.write_typed("log.json", &log_entry)?; Ok(()) }

JSON Validation and Transformation

// JSON validation and transformation
struct JsonValidator {
    rules: std::collections::HashMap<String, Box<dyn ValidationRule>>,
}

trait ValidationRule { fn validate(&self, data: &serde_json::Value) -> Result<(), String>; fn transform(&self, data: &serde_json::Value) -> Result<serde_json::Value, String>; }

impl JsonValidator { fn new() -> Self { let mut validator = Self { rules: std::collections::HashMap::new(), }; // Register validation rules validator.register_rule("email", Box::new(EmailValidationRule)); validator.register_rule("url", Box::new(UrlValidationRule)); validator.register_rule("date", Box::new(DateValidationRule)); validator } fn register_rule(&mut self, name: &str, rule: Box<dyn ValidationRule>) { self.rules.insert(name.to_string(), rule); } fn validate_json(&self, data: &serde_json::Value, rules: &[String]) -> Result<(), Vec<String>> { let mut errors = Vec::new(); for rule_name in rules { if let Some(rule) = self.rules.get(rule_name) { if let Err(error) = rule.validate(data) { errors.push(format!("{}: {}", rule_name, error)); } } } if errors.is_empty() { Ok(()) } else { Err(errors) } } fn transform_json(&self, data: &serde_json::Value, rules: &[String]) -> Result<serde_json::Value, Box<dyn std::error::Error>> { let mut transformed = data.clone(); for rule_name in rules { if let Some(rule) = self.rules.get(rule_name) { transformed = rule.transform(&transformed)?; } } Ok(transformed) } }

// Email validation rule struct EmailValidationRule;

impl ValidationRule for EmailValidationRule { fn validate(&self, data: &serde_json::Value) -> Result<(), String> { if let Some(email) = data.as_str() { if !email.contains('@') || !email.contains('.') { return Err("Invalid email format".to_string()); } } else { return Err("Email must be a string".to_string()); } Ok(()) } fn transform(&self, data: &serde_json::Value) -> Result<serde_json::Value, String> { if let Some(email) = data.as_str() { Ok(serde_json::Value::String(email.to_lowercase())) } else { Err("Email must be a string".to_string()) } } }

// URL validation rule struct UrlValidationRule;

impl ValidationRule for UrlValidationRule { fn validate(&self, data: &serde_json::Value) -> Result<(), String> { if let Some(url) = data.as_str() { if !url.starts_with("http://") && !url.starts_with("https://") { return Err("URL must start with http:// or https://".to_string()); } } else { return Err("URL must be a string".to_string()); } Ok(()) } fn transform(&self, data: &serde_json::Value) -> Result<serde_json::Value, String> { if let Some(url) = data.as_str() { // Normalize URL let normalized = if !url.starts_with("http://") && !url.starts_with("https://") { format!("https://{}", url) } else { url.to_string() }; Ok(serde_json::Value::String(normalized)) } else { Err("URL must be a string".to_string()) } } }

// Date validation rule struct DateValidationRule;

impl ValidationRule for DateValidationRule { fn validate(&self, data: &serde_json::Value) -> Result<(), String> { if let Some(date_str) = data.as_str() { if chrono::DateTime::parse_from_rfc3339(date_str).is_err() { return Err("Invalid date format. Expected RFC3339".to_string()); } } else { return Err("Date must be a string".to_string()); } Ok(()) } fn transform(&self, data: &serde_json::Value) -> Result<serde_json::Value, String> { if let Some(date_str) = data.as_str() { if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(date_str) { Ok(serde_json::Value::String(datetime.to_rfc3339())) } else { Err("Invalid date format".to_string()) } } else { Err("Date must be a string".to_string()) } } }

Best Practices

1. Always Validate JSON Data

// Validate JSON data before processing
fn safe_json_operation(path: &str) -> Result<(), Box<dyn std::error::Error>> {
    let mut json_manager = JsonFileManager::new("/safe/path")?;
    let schema_manager = JsonSchemaManager::new();
    
    // Load and validate schema
    let schema = schema_manager.get_schema("config").unwrap();
    
    // Read and validate JSON
    let data = json_manager.read_json_validate(path, schema)?;
    
    // Process validated data
    process_config_data(data)?;
    
    Ok(())
}

2. Use Type-Safe Operations

// Use type-safe JSON operations
fn typed_json_handling() -> Result<(), Box<dyn std::error::Error>> {
    let typed_manager = TypedJsonManager::new();
    
    // Read with type safety
    let config: AppConfig = typed_manager.read_typed("config.json")?;
    
    // Modify and write back
    let mut updated_config = config;
    updated_config.debug = true;
    typed_manager.write_typed("config.json", &updated_config)?;
    
    Ok(())
}

3. Implement JSON Caching

// Cache JSON data for performance
fn cached_json_read(path: &str) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
    let cache_key = format!("json:{}", path);
    
    // Check cache first
    if let Some(cached) = @cache.get(&cache_key) {
        return Ok(cached);
    }
    
    // Read JSON file
    let mut json_manager = JsonFileManager::new("/base/path")?;
    let data = json_manager.read_json(path)?;
    
    // Cache for 10 minutes
    @cache.put(&cache_key, &data, std::time::Duration::from_secs(600))?;
    
    Ok(data)
}

4. Validate JSON Schema

// Validate JSON against schema
fn validate_json_schema(data: &serde_json::Value, schema_name: &str) -> Result<(), Box<dyn std::error::Error>> {
    let schema_manager = JsonSchemaManager::new();
    
    // Load common schemas
    let schemas = get_common_schemas();
    for (name, schema) in schemas {
        schema_manager.register_schema(&name, schema)?;
    }
    
    // Validate data
    schema_manager.validate_data(schema_name, data)?;
    
    Ok(())
}

5. Use Pretty Formatting for Human-Readable Files

// Use pretty formatting for configuration files
fn write_config_file(config: &AppConfig, path: &str) -> Result<(), Box<dyn std::error::Error>> {
    let json_manager = JsonFileManager::new("/config")?;
    
    // Convert to JSON value
    let json_data = serde_json::to_value(config)?;
    
    // Write with pretty formatting
    json_manager.write_json_pretty(path, &json_data)?;
    
    Ok(())
}

The @json_file function in Rust provides comprehensive JSON file operations with schema validation, type safety, and high performance.