cfgmatic-source 5.0.1

Configuration sources (file, env, memory) for cfgmatic framework
Documentation
//! Result types for [`SourceCoordinator`](super::SourceCoordinator).

use cfgmatic_merge::MergeReport;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};

use crate::domain::{ParsedContent, Result, SourceMetadata};

/// Parsed configuration layer collected before merge.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SourceLayer {
    /// Source metadata.
    pub metadata: SourceMetadata,
    /// Source priority. Higher priority layers win.
    pub priority: i32,
    /// Source registration order for tie-breaking.
    pub registration_index: usize,
    /// Parsed layer content.
    pub content: ParsedContent,
}

impl SourceLayer {
    /// Return a stable source identifier for reporting.
    #[must_use]
    pub fn source_id(&self) -> String {
        self.metadata.display_id()
    }
}

/// Result of loading configuration from multiple sources.
#[derive(Debug, Clone)]
pub struct LoadResult {
    /// Merged configuration content.
    pub content: ParsedContent,

    /// Number of sources that were successfully loaded.
    pub loaded_count: usize,

    /// Names of sources that were loaded.
    pub loaded_sources: Vec<String>,

    /// Names of sources that failed (if not fail-fast).
    pub failed_sources: Vec<(String, String)>,

    /// Total processing time in milliseconds.
    pub processing_time_ms: u64,
}

/// Rich report for source loading and provenance-aware merge.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LoadReport {
    /// Parsed layers in merge application order.
    pub layers: Vec<SourceLayer>,
    /// Final merged content.
    pub merged: ParsedContent,
    /// Provenance-aware merge report.
    pub merge_report: MergeReport,
    /// Number of sources that were successfully loaded.
    pub loaded_count: usize,
    /// Names of sources that were loaded.
    pub loaded_sources: Vec<String>,
    /// Names of sources that failed (if not fail-fast).
    pub failed_sources: Vec<(String, String)>,
    /// Total processing time in milliseconds.
    pub processing_time_ms: u64,
}

impl LoadReport {
    /// Convert the report into the lightweight legacy result.
    #[must_use]
    pub fn into_load_result(self) -> LoadResult {
        LoadResult {
            content: self.merged,
            loaded_count: self.loaded_count,
            loaded_sources: self.loaded_sources,
            failed_sources: self.failed_sources,
            processing_time_ms: self.processing_time_ms,
        }
    }

    /// Convert the merged content into a specific type.
    ///
    /// # Errors
    ///
    /// Returns an error if deserialization fails.
    pub fn to_type<T: DeserializeOwned>(&self) -> Result<T> {
        self.merged.to_type()
    }
}

impl LoadResult {
    /// Create a new load result.
    #[must_use]
    pub const fn new(content: ParsedContent) -> Self {
        Self {
            content,
            loaded_count: 0,
            loaded_sources: Vec::new(),
            failed_sources: Vec::new(),
            processing_time_ms: 0,
        }
    }

    /// Get the merged configuration content.
    #[must_use]
    pub const fn content(&self) -> &ParsedContent {
        &self.content
    }

    /// Get the number of successfully loaded sources.
    #[must_use]
    pub const fn loaded_count(&self) -> usize {
        self.loaded_count
    }

    /// Check if any sources failed.
    #[must_use]
    pub const fn has_failures(&self) -> bool {
        !self.failed_sources.is_empty()
    }

    /// Convert to a specific type.
    ///
    /// # Errors
    ///
    /// Returns an error if deserialization fails.
    pub fn to_type<T: DeserializeOwned>(&self) -> Result<T> {
        self.content.to_type()
    }
}