---
to: "{{ output_file | default(value='generated/safe_error_handling.rs') }}"
description: "Safe error handling patterns template"
category: "dogfooding"
---
// Safe Error Handling Patterns
// Generated by ggen dogfooding template
//
// This module demonstrates safe error handling patterns
// that replace panic-prone .expect() and .unwrap() calls

use anyhow::{Context, Result};
use std::fs;
use std::path::Path;

{% if function_name %}
/// {{ function_description | default(value='Performs an operation safely') }}
///
/// # Errors
/// Returns an error if the operation fails
pub fn {{ function_name }}({% for param in parameters %}{{ param.name }}: {{ param.type }}{% if not loop.last %}, {% endif %}{% endfor %}) -> Result<{{ return_type | default(value='()') }}> {
    {% for step in steps %}
    // {{ step.description }}
    {% if step.operation_type == "file_read" %}
    let content = fs::read_to_string({{ step.path }})
        .with_context(|| format!("Failed to read file: {}", {{ step.path }}))?;
    {% elif step.operation_type == "file_write" %}
    fs::write({{ step.path }}, {{ step.content }})
        .with_context(|| format!("Failed to write file: {}", {{ step.path }}))?;
    {% elif step.operation_type == "parse" %}
    let {{ step.var_name }} = {{ step.expression }}
        .map_err(|e| anyhow::anyhow!("Failed to parse: {}", e))?;
    {% elif step.operation_type == "convert" %}
    let {{ step.var_name }} = {{ step.expression }}
        .ok_or_else(|| anyhow::anyhow!("Conversion failed"))?;
    {% elif step.operation_type == "custom" %}
    {{ step.code }}
    {% endif %}
    {% endfor %}

    {% if return_expression %}
    Ok({{ return_expression }})
    {% else %}
    Ok(())
    {% endif %}
}
{% endif %}

// ============================================================================
// Common Safe Patterns
// ============================================================================

/// Safe file reading with context
pub fn read_file_safe(path: impl AsRef<Path>) -> Result<String> {
    fs::read_to_string(path.as_ref())
        .with_context(|| format!("Failed to read file: {:?}", path.as_ref()))
}

/// Safe file writing with context
pub fn write_file_safe(path: impl AsRef<Path>, content: impl AsRef<str>) -> Result<()> {
    fs::write(path.as_ref(), content.as_ref())
        .with_context(|| format!("Failed to write file: {:?}", path.as_ref()))
}

/// Safe JSON parsing
pub fn parse_json_safe<T: serde::de::DeserializeOwned>(json: &str) -> Result<T> {
    serde_json::from_str(json)
        .map_err(|e| anyhow::anyhow!("Failed to parse JSON: {}", e))
}

/// Safe time operations
pub fn current_timestamp_safe() -> u64 {
    std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map(|d| d.as_secs())
        .unwrap_or_else(|_| {
            tracing::warn!("System clock error, using fallback");
            0
        })
}

/// Safe optional unwrapping with logging
pub fn unwrap_or_log<T: std::fmt::Debug>(opt: Option<T>, default: T, context: &str) -> T {
    opt.unwrap_or_else(|| {
        tracing::warn!("Using default value for {}: {:?}", context, default);
        default
    })
}

/// Safe result unwrapping with error logging
pub fn unwrap_or_log_error<T, E: std::fmt::Display>(
    result: Result<T, E>,
    default: T,
    context: &str,
) -> T {
    result.unwrap_or_else(|e| {
        tracing::error!("Error in {}: {}", context, e);
        default
    })
}

// ============================================================================
// Pattern Examples
// ============================================================================

/// Example: Safe configuration loading
pub fn load_config_safe(path: impl AsRef<Path>) -> Result<serde_json::Value> {
    // ❌ BAD: let content = fs::read_to_string(path).expect("Failed to read config");

    // ✅ GOOD: Use proper error handling with context
    let content = fs::read_to_string(path.as_ref())
        .with_context(|| format!("Failed to read config file: {:?}", path.as_ref()))?;

    // ❌ BAD: let config = serde_json::from_str(&content).unwrap();

    // ✅ GOOD: Propagate error with context
    let config = serde_json::from_str(&content)
        .map_err(|e| anyhow::anyhow!("Failed to parse config JSON: {}", e))?;

    Ok(config)
}

/// Example: Safe command execution
pub fn execute_command_safe(cmd: &str, args: &[&str]) -> Result<String> {
    // ❌ BAD: let output = Command::new(cmd).args(args).output().expect("Command failed");

    // ✅ GOOD: Handle errors gracefully
    let output = std::process::Command::new(cmd)
        .args(args)
        .output()
        .with_context(|| format!("Failed to execute command: {} {:?}", cmd, args))?;

    // ❌ BAD: let stdout = String::from_utf8(output.stdout).unwrap();

    // ✅ GOOD: Handle conversion errors
    let stdout = String::from_utf8(output.stdout)
        .map_err(|e| anyhow::anyhow!("Invalid UTF-8 in command output: {}", e))?;

    Ok(stdout)
}

/// Example: Safe timestamp operations
pub fn safe_timestamp_example() -> Result<String> {
    // ❌ BAD:
    // let timestamp = SystemTime::now()
    //     .duration_since(UNIX_EPOCH)
    //     .expect("System clock error")
    //     .as_secs();

    // ✅ GOOD: Use safe fallback
    let timestamp = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map(|d| d.as_secs())
        .unwrap_or_else(|_| {
            tracing::warn!("System clock error, using fallback timestamp");
            0
        });

    Ok(format!("Timestamp: {}", timestamp))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_safe_patterns() {
        // These tests demonstrate that safe patterns handle errors gracefully

        // Test safe file reading with non-existent file
        let result = read_file_safe("/nonexistent/file.txt");
        assert!(result.is_err());

        // Test safe timestamp (always succeeds)
        let timestamp = current_timestamp_safe();
        assert!(timestamp >= 0);

        // Test safe optional unwrapping
        let value = unwrap_or_log(None, 42, "test");
        assert_eq!(value, 42);
    }
}
