luff 0.2.1

Print files with formatting
Documentation
//! Schema generation for config file validation

use std::fs;
use std::io::{self, Write};
use std::path::{Path, PathBuf};

use schemars::schema_for;

/// Writes the generated schema to standard output.
///
/// # Errors
///
/// Returns `crate::error::Error::Io` if writing to the standard output stream fails
/// (for example, if the pipe is closed or broken, resulting in an `EPIPE` error).
fn write_schema_to_stdout(schema_json: &str) -> Result<(), crate::error::Error> {
    io::stdout()
        .write_all(schema_json.as_bytes())
        .map_err(crate::error::Error::Io)
}

/// Writes the generated schema to the specified filesystem path.
///
/// # Security
///
/// **OWASP CWE-22 (Path Traversal):** This function assumes `path` has already
/// been validated and sanitized by the caller. Do not pass raw, unvalidated user
/// input into this function to prevent arbitrary file overwrites.
///
/// If the schema contains sensitive infrastructure details, ensure the destination
/// directory has restrictive file permissions (e.g., `0o600`).
///
/// # Errors
///
/// Returns `crate::error::Error::Io` if the file cannot be created or written to.
/// The error is contextually enriched with the target file path to aid in
/// observability and debugging.
fn write_schema_to_file(path: &Path, schema_json: &str) -> Result<(), crate::error::Error> {
    fs::write(path, schema_json).map_err(|e| {
        // Enriched error context aids in distributed tracing/debugging
        crate::error::Error::Io(io::Error::other(format!(
            "Failed to write schema to {}: {e}",
            path.display()
        )))
    })
}

/// Generate JSON schema for config files
///
/// # Arguments
///
/// * `output` - Optional output file path (stdout if None)
///
/// # Errors
///
/// Returns an error if schema serialization or file/stdout writing fails.
pub fn generate_schema(output: Option<&PathBuf>) -> crate::error::Result<()> {
    let schema = schema_for!(crate::config::ConfigFile);
    let schema_json =
        serde_json::to_string_pretty(&schema).map_err(|e| crate::error::Error::Config {
            message: format!("Failed to serialize JSON schema: {e}"),
        })?;

    output.map_or_else(
        || write_schema_to_stdout(&schema_json),
        |path| write_schema_to_file(path, &schema_json),
    )
}