Skip to main content

superstac_core/models/
settings.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{errors::ValidationError, models::catalog::HealthCheckFrequencyStrategy};
4
5#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
6#[serde(rename_all = "lowercase")]
7pub enum LogLevel {
8    Info,
9    Warning,
10    Debug,
11}
12
13/// Workspace-wide settings. Loaded from YAML (`settings:` block) or built
14/// from [`Settings::default`]. Field-level docs cover each knob.
15#[derive(Clone, Debug, Serialize, Deserialize)]
16pub struct Settings {
17    pub health_check_strategy: HealthCheckFrequencyStrategy,
18    /// Status codes considered "healthy" (inclusive). Default `(200, 299)`.
19    pub healthy_status_code_range: (u16, u16),
20    /// When true, duplicate catalog ids get auto-suffixed instead of erroring.
21    pub auto_fix_duplicate_catalog_id: bool,
22    /// Same as above, for provider ids.
23    pub auto_fix_duplicate_provider_id: bool,
24    pub log_level: LogLevel,
25    /// Disable tracing-subscriber init entirely when false.
26    pub logging_enabled: bool,
27    /// Drop unhealthy catalogs from federated search. Default true.
28    pub search_healthy_catalogs_only: Option<bool>,
29    /// Collapse items sharing the same `Item.id` across catalogs into one,
30    /// with provenance tracked via `SearchItem.seen_in`. Defaults to true.
31    pub deduplicate_items: Option<bool>,
32    /// Rewrite each item's `collection` and `assets` to canonical names
33    /// using each catalog's alias maps. Defaults to true.
34    pub unify_response: Option<bool>,
35    /// Maximum number of catalogs to query concurrently. Defaults to 8.
36    pub max_concurrent_catalogs: Option<usize>,
37    /// Per-catalog request timeout in seconds. Defaults to 30.
38    pub per_catalog_timeout_seconds: Option<u64>,
39    /// Maximum attempts per catalog (1 = no retry). Defaults to 2.
40    pub max_retry_attempts: Option<u8>,
41    /// Initial retry backoff in milliseconds. Defaults to 100.
42    pub retry_initial_backoff_ms: Option<u64>,
43    /// Maximum retry backoff in milliseconds (caps exponential growth). Defaults to 2000.
44    pub retry_max_backoff_ms: Option<u64>,
45    /// Hard cap on items returned per catalog (prevents runaway pagination).
46    /// Defaults to 1000.
47    pub max_items_per_catalog: Option<usize>,
48}
49
50impl Default for Settings {
51    fn default() -> Self {
52        Self {
53            health_check_strategy: HealthCheckFrequencyStrategy::Hourly,
54            healthy_status_code_range: (200, 299),
55            auto_fix_duplicate_catalog_id: true,
56            auto_fix_duplicate_provider_id: true,
57            log_level: LogLevel::Info,
58            logging_enabled: true,
59            search_healthy_catalogs_only: Some(true),
60            deduplicate_items: Some(true),
61            unify_response: Some(true),
62            max_concurrent_catalogs: Some(8),
63            per_catalog_timeout_seconds: Some(30),
64            max_retry_attempts: Some(2),
65            retry_initial_backoff_ms: Some(100),
66            retry_max_backoff_ms: Some(2000),
67            max_items_per_catalog: Some(1000),
68        }
69    }
70}
71
72/// Partial [`Settings`] update — `None` fields are left alone.
73#[derive(Clone, Debug, Serialize, Deserialize)]
74pub struct SettingsUpdate {
75    pub health_check_strategy: Option<HealthCheckFrequencyStrategy>,
76    pub healthy_status_code_range: Option<(u16, u16)>,
77    pub auto_fix_duplicate_catalog_id: Option<bool>,
78    pub auto_fix_duplicate_provider_id: Option<bool>,
79    pub log_level: Option<LogLevel>,
80    pub logging_enabled: Option<bool>,
81    pub search_healthy_catalogs_only: Option<bool>,
82    pub deduplicate_items: Option<bool>,
83    pub unify_response: Option<bool>,
84    pub max_concurrent_catalogs: Option<usize>,
85    pub per_catalog_timeout_seconds: Option<u64>,
86    pub max_retry_attempts: Option<u8>,
87    pub retry_initial_backoff_ms: Option<u64>,
88    pub retry_max_backoff_ms: Option<u64>,
89    pub max_items_per_catalog: Option<usize>,
90}
91
92impl TryFrom<Settings> for SettingsUpdate {
93    type Error = ValidationError;
94    fn try_from(value: Settings) -> Result<Self, Self::Error> {
95        Ok(SettingsUpdate {
96            healthy_status_code_range: Some(value.healthy_status_code_range),
97            health_check_strategy: Some(value.health_check_strategy),
98            auto_fix_duplicate_catalog_id: Some(value.auto_fix_duplicate_catalog_id),
99            auto_fix_duplicate_provider_id: Some(value.auto_fix_duplicate_provider_id),
100            log_level: Some(value.log_level),
101            logging_enabled: Some(value.logging_enabled),
102            search_healthy_catalogs_only: value.search_healthy_catalogs_only,
103            deduplicate_items: value.deduplicate_items,
104            unify_response: value.unify_response,
105            max_concurrent_catalogs: value.max_concurrent_catalogs,
106            per_catalog_timeout_seconds: value.per_catalog_timeout_seconds,
107            max_retry_attempts: value.max_retry_attempts,
108            retry_initial_backoff_ms: value.retry_initial_backoff_ms,
109            retry_max_backoff_ms: value.retry_max_backoff_ms,
110            max_items_per_catalog: value.max_items_per_catalog,
111        })
112    }
113}