webpage_quality_analyzer 1.0.2

High-performance webpage quality analyzer with 115 comprehensive metrics - Rust library with WASM, C++, and Python bindings
Documentation
// src/config/config_models.rs - GENERATED FROM CENTRALIZED METRIC REGISTRY

use crate::config::enhanced_models::EnhancedScoringProfile;
use crate::models::models::{ExtractedMetadata, PageMetrics, ProcessedDocument};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Validation error for configuration values
#[derive(Debug, thiserror::Error)]
pub enum ValidationError {
    #[error("Invalid weight value for '{field}': {value} (must be between 0.0 and 1.0)")]
    InvalidWeight { field: String, value: f32 },

    #[error("Invalid range for '{field}': min={min}, max={max} (min must be <= max)")]
    InvalidRange { field: String, min: f32, max: f32 },

    #[error("Invalid threshold value for '{field}': {value} (must be between {min} and {max})")]
    InvalidThreshold {
        field: String,
        value: f32,
        min: f32,
        max: f32,
    },

    #[error("Profile weights do not sum to approximately 1.0: sum={sum}")]
    WeightSumError { sum: f32 },

    #[error("Missing required field: '{field}'")]
    MissingField { field: String },

    #[error("Invalid value for '{field}': {message}")]
    InvalidValue { field: String, message: String },
}

impl From<String> for ValidationError {
    fn from(message: String) -> Self {
        ValidationError::InvalidValue {
            field: "unknown".to_string(),
            message,
        }
    }
}

/// Root configuration structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProfileConfig {
    /// Profile name to activate (e.g., "content_article", "news", "product", or "custom")
    pub active_profile: Option<String>,

    /// Named preset profiles available to users (including defaults)
    #[serde(default)]
    pub presets: HashMap<String, EnhancedScoringProfile>,

    /// Global output controls and defaults
    #[serde(default)]
    pub output: OutputOptions,

    /// Quality band configuration (cutoffs)
    #[serde(default)]
    pub bands: QualityBandConfig,

    /// Optional plugin configuration
    #[serde(default)]
    pub plugins: PluginConfig,
}

impl Default for ProfileConfig {
    fn default() -> Self {
        Self {
            active_profile: Some("content_article".to_string()),
            presets: HashMap::new(),
            output: OutputOptions::default(),
            bands: QualityBandConfig::default(),
            plugins: PluginConfig::default(),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OutputOptions {
    /// Include debug information in output
    pub include_debug: bool,

    /// Include raw metrics in output
    pub include_raw_metrics: bool,

    /// Format for timestamps
    pub timestamp_format: String,

    /// Precision for floating-point numbers
    pub float_precision: usize,
}

impl Default for OutputOptions {
    fn default() -> Self {
        Self {
            include_debug: false,
            include_raw_metrics: false,
            timestamp_format: "%Y-%m-%dT%H:%M:%SZ".to_string(),
            float_precision: 3,
        }
    }
}

/// Quality band configuration (score ranges)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualityBandConfig {
    /// Score range for excellent quality
    pub excellent: (f32, f32),

    /// Score range for good quality
    pub good: (f32, f32),

    /// Score range for fair quality
    pub fair: (f32, f32),

    /// Score range for poor quality (below this is very poor)
    pub poor: (f32, f32),
}

impl Default for QualityBandConfig {
    fn default() -> Self {
        Self {
            excellent: (0.9, 1.0),
            good: (0.7, 0.9),
            fair: (0.5, 0.7),
            poor: (0.3, 0.5),
            // Anything below 0.3 is "very poor"
        }
    }
}

/// Plugin configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginConfig {
    /// Enabled plugins
    pub enabled: Vec<String>,

    /// Plugin-specific configuration
    pub settings: HashMap<String, serde_json::Value>,
}

impl Default for PluginConfig {
    fn default() -> Self {
        Self {
            enabled: Vec::new(),
            settings: HashMap::new(),
        }
    }
}

/// Verbosity level for logging and output
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Verbosity {
    Silent,
    Normal,
    Verbose,
    Debug,
}

impl Default for Verbosity {
    fn default() -> Self {
        Verbosity::Normal
    }
}

/// Plugin registry for managing available plugins
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginRegistry {
    pub plugins: HashMap<String, PluginInfo>,
}

impl Default for PluginRegistry {
    fn default() -> Self {
        Self {
            plugins: HashMap::new(),
        }
    }
}

impl PluginRegistry {
    pub fn new() -> Self {
        Self::default()
    }
}

/// Information about a plugin
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginInfo {
    pub name: String,
    pub version: String,
    pub description: String,
    pub enabled: bool,
}

/// Trait for score plugins
pub trait ScorePlugin {
    fn name(&self) -> &str;
    fn version(&self) -> &str {
        "1.0.0"
    }

    fn apply(
        &self,
        metrics: &PageMetrics,
        meta: &ExtractedMetadata,
        doc: &ProcessedDocument,
        profile: &EnhancedScoringProfile,
    ) -> (f32, Vec<String>);

    fn calculate_score(&self, metrics: &PageMetrics) -> Result<f32, String> {
        // Default implementation that uses apply
        let (score, _) = self.apply(
            metrics,
            &ExtractedMetadata::default(),
            &ProcessedDocument::default(),
            &EnhancedScoringProfile::default(),
        );
        Ok(score)
    }
}