Skip to main content

hdds_persistence/
config.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2// Copyright (c) 2025-2026 naskel.com
3
4//! Persistence service configuration
5
6use serde::{Deserialize, Serialize};
7
8/// Persistence service configuration
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct Config {
11    /// Topic filter (supports wildcards: "State/*", "*")
12    pub topic_filter: String,
13
14    /// Retention policy: maximum number of samples to keep per topic
15    pub retention_count: usize,
16
17    /// Retention policy: maximum age of samples in seconds (0 = infinite)
18    pub retention_time_secs: u64,
19
20    /// Retention policy: maximum total storage size in bytes (0 = infinite)
21    pub retention_size_bytes: u64,
22
23    /// Domain ID to join
24    pub domain_id: u32,
25
26    /// Participant name
27    pub participant_name: String,
28
29    /// Subscribe to volatile writers (default: false, only TRANSIENT_LOCAL)
30    pub subscribe_volatile: bool,
31}
32
33impl Default for Config {
34    fn default() -> Self {
35        Self {
36            topic_filter: "*".to_string(),
37            retention_count: 10000,
38            retention_time_secs: 0,
39            retention_size_bytes: 0,
40            domain_id: 0,
41            participant_name: "PersistenceService".to_string(),
42            subscribe_volatile: false,
43        }
44    }
45}
46
47impl Config {
48    /// Create a new config builder
49    pub fn builder() -> ConfigBuilder {
50        ConfigBuilder::default()
51    }
52}
53
54/// Config builder for fluent API
55#[derive(Debug, Default)]
56pub struct ConfigBuilder {
57    topic_filter: Option<String>,
58    retention_count: Option<usize>,
59    retention_time_secs: Option<u64>,
60    retention_size_bytes: Option<u64>,
61    domain_id: Option<u32>,
62    participant_name: Option<String>,
63    subscribe_volatile: Option<bool>,
64}
65
66impl ConfigBuilder {
67    /// Set topic filter (supports wildcards: "State/*", "*")
68    pub fn topic_filter(mut self, filter: impl Into<String>) -> Self {
69        self.topic_filter = Some(filter.into());
70        self
71    }
72
73    /// Set retention count (maximum samples per topic)
74    pub fn retention_count(mut self, count: usize) -> Self {
75        self.retention_count = Some(count);
76        self
77    }
78
79    /// Set retention time in seconds (0 = infinite)
80    pub fn retention_time_secs(mut self, secs: u64) -> Self {
81        self.retention_time_secs = Some(secs);
82        self
83    }
84
85    /// Set retention size in bytes (0 = infinite)
86    pub fn retention_size_bytes(mut self, bytes: u64) -> Self {
87        self.retention_size_bytes = Some(bytes);
88        self
89    }
90
91    /// Set domain ID
92    pub fn domain_id(mut self, id: u32) -> Self {
93        self.domain_id = Some(id);
94        self
95    }
96
97    /// Set participant name
98    pub fn participant_name(mut self, name: impl Into<String>) -> Self {
99        self.participant_name = Some(name.into());
100        self
101    }
102
103    /// Subscribe to volatile writers (default: false)
104    pub fn subscribe_volatile(mut self, subscribe: bool) -> Self {
105        self.subscribe_volatile = Some(subscribe);
106        self
107    }
108
109    /// Build the configuration
110    pub fn build(self) -> Config {
111        let defaults = Config::default();
112
113        Config {
114            topic_filter: self.topic_filter.unwrap_or(defaults.topic_filter),
115            retention_count: self.retention_count.unwrap_or(defaults.retention_count),
116            retention_time_secs: self
117                .retention_time_secs
118                .unwrap_or(defaults.retention_time_secs),
119            retention_size_bytes: self
120                .retention_size_bytes
121                .unwrap_or(defaults.retention_size_bytes),
122            domain_id: self.domain_id.unwrap_or(defaults.domain_id),
123            participant_name: self.participant_name.unwrap_or(defaults.participant_name),
124            subscribe_volatile: self
125                .subscribe_volatile
126                .unwrap_or(defaults.subscribe_volatile),
127        }
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    fn test_config_builder() {
137        let config = Config::builder()
138            .topic_filter("State/*")
139            .retention_count(500)
140            .retention_time_secs(3600)
141            .domain_id(42)
142            .participant_name("TestPersistence")
143            .build();
144
145        assert_eq!(config.topic_filter, "State/*");
146        assert_eq!(config.retention_count, 500);
147        assert_eq!(config.retention_time_secs, 3600);
148        assert_eq!(config.domain_id, 42);
149        assert_eq!(config.participant_name, "TestPersistence");
150    }
151
152    #[test]
153    fn test_config_defaults() {
154        let config = Config::default();
155
156        assert_eq!(config.topic_filter, "*");
157        assert_eq!(config.retention_count, 10000);
158        assert_eq!(config.retention_time_secs, 0);
159        assert_eq!(config.domain_id, 0);
160    }
161}