use crate::config::enhanced_models::EnhancedScoringProfile;
use crate::models::models::{ExtractedMetadata, PageMetrics, ProcessedDocument};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[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,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProfileConfig {
pub active_profile: Option<String>,
#[serde(default)]
pub presets: HashMap<String, EnhancedScoringProfile>,
#[serde(default)]
pub output: OutputOptions,
#[serde(default)]
pub bands: QualityBandConfig,
#[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 {
pub include_debug: bool,
pub include_raw_metrics: bool,
pub timestamp_format: String,
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,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualityBandConfig {
pub excellent: (f32, f32),
pub good: (f32, f32),
pub fair: (f32, f32),
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),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginConfig {
pub enabled: Vec<String>,
pub settings: HashMap<String, serde_json::Value>,
}
impl Default for PluginConfig {
fn default() -> Self {
Self {
enabled: Vec::new(),
settings: HashMap::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Verbosity {
Silent,
Normal,
Verbose,
Debug,
}
impl Default for Verbosity {
fn default() -> Self {
Verbosity::Normal
}
}
#[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()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginInfo {
pub name: String,
pub version: String,
pub description: String,
pub enabled: bool,
}
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> {
let (score, _) = self.apply(
metrics,
&ExtractedMetadata::default(),
&ProcessedDocument::default(),
&EnhancedScoringProfile::default(),
);
Ok(score)
}
}