hopr_chain_indexer/
config.rs

1/// Configuration for the chain indexer functionality
2#[derive(Debug, Clone, smart_default::SmartDefault)]
3pub struct IndexerConfig {
4    /// The block at which the indexer should start
5    ///
6    /// It typically makes little sense to start indexing from the beginning
7    /// of the chain; all that is sufficient is to start indexing since the
8    /// relevant smart contracts were introduced into the chain.
9    ///
10    /// This value makes sure that indexing is relevant and as minimal as possible.
11    ///
12    /// Default is `0`.
13    pub start_block_number: u64,
14
15    /// Whether to use fast synchronization during indexing.
16    /// When enabled, it allows for quicker indexing of existing logs during node startup.
17    ///
18    /// Default is `true`.
19    #[default(true)]
20    pub fast_sync: bool,
21
22    /// Whether to perform logs snapshot download on startup.
23    /// When enabled, it allows for quicker indexing from scratch.
24    ///
25    /// Default is `false`.
26    #[default(false)]
27    pub enable_logs_snapshot: bool,
28
29    /// URL to download logs snapshot from.
30    /// This should point to a publicly accessible tar.xz file containing
31    /// the SQLite logs database files.
32    ///
33    /// Default is None
34    #[default(None)]
35    pub logs_snapshot_url: Option<String>,
36
37    /// Path to the data directory where databases are stored.
38    /// This is used for snapshot installation and database state checking.
39    ///
40    /// Default is empty string (must be set by application).
41    pub data_directory: String,
42}
43
44impl IndexerConfig {
45    /// Creates a new indexer configuration.
46    ///
47    /// # Arguments
48    ///
49    /// * `start_block_number` - The block number from which to start indexing
50    /// * `fast_sync` - Whether to enable fast synchronization during startup
51    /// * `enable_logs_snapshot` - Whether to enable logs snapshot download
52    /// * `logs_snapshot_url` - URL to download logs snapshot from
53    /// * `data_directory` - Path to the data directory where databases are stored
54    ///
55    /// # Returns
56    ///
57    /// A new instance of `IndexerConfig`
58    pub fn new(
59        start_block_number: u64,
60        fast_sync: bool,
61        enable_logs_snapshot: bool,
62        logs_snapshot_url: Option<String>,
63        data_directory: String,
64    ) -> Self {
65        Self {
66            start_block_number,
67            fast_sync,
68            enable_logs_snapshot,
69            logs_snapshot_url,
70            data_directory,
71        }
72    }
73
74    /// Validates the configuration and returns any validation errors.
75    ///
76    /// Performs comprehensive validation of configuration parameters including:
77    /// - URL format and protocol validation (HTTP/HTTPS/file:// supported)
78    /// - File extension validation (.tar.xz required)
79    /// - Data directory path validation
80    /// - Dependency validation (data directory required when snapshots enabled)
81    ///
82    /// # Returns
83    ///
84    /// - `Ok(())` if all validation passes
85    /// - `Err(String)` with a descriptive error message if validation fails
86    ///
87    /// # Example
88    ///
89    /// ```
90    /// use hopr_chain_indexer::IndexerConfig;
91    ///
92    /// let config = IndexerConfig::new(
93    ///     100,
94    ///     true,
95    ///     true,
96    ///     Some("https://example.com/snapshot.tar.xz".to_string()),
97    ///     "/tmp/hopr_data".to_string(),
98    /// );
99    ///
100    /// assert!(config.validate().is_ok());
101    /// ```
102    pub fn validate(&self) -> Result<(), String> {
103        // Check that url is valid if logs snapshot is enabled
104        if self.enable_logs_snapshot && self.logs_snapshot_url.is_none() {
105            return Err("Logs snapshot URL must be specified when logs snapshots are enabled".to_string());
106        }
107
108        Ok(())
109    }
110
111    /// Convenience method to check if the configuration is valid.
112    ///
113    /// This is a simple wrapper around `validate()` that returns a boolean
114    /// instead of a `Result`, making it easier to use in conditional expressions.
115    ///
116    /// # Returns
117    ///
118    /// `true` if all validation passes, `false` otherwise
119    ///
120    /// # Example
121    ///
122    /// ```
123    /// use hopr_chain_indexer::IndexerConfig;
124    ///
125    /// let config = IndexerConfig::default();
126    /// if !config.is_valid() {
127    ///     // Handle invalid configuration
128    /// }
129    /// ```
130    pub fn is_valid(&self) -> bool {
131        self.validate().is_ok()
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138
139    #[test]
140    fn test_full_valid_config() {
141        let data_directory = "/tmp/hopr_test_data";
142        let logs_snapshot_url = format!("file:///tmp/snapshot.tar.xz");
143
144        let cfg = IndexerConfig::new(0, true, true, Some(logs_snapshot_url), data_directory.into());
145
146        cfg.validate().expect("Failed to validate snapshot configuration");
147        assert!(cfg.is_valid(), "Valid configuration should return true for is_valid()");
148    }
149
150    #[test]
151    fn test_disabled_snapshot_config() {
152        let cfg = IndexerConfig::new(0, true, false, Some("".to_string()), "".to_string());
153
154        assert!(
155            cfg.validate().is_ok(),
156            "Configuration should be valid when snapshots disabled"
157        );
158    }
159
160    #[test]
161    fn test_missing_snapshot_url() {
162        let cfg = IndexerConfig::new(0, true, true, None, "".to_string());
163
164        assert!(
165            cfg.validate().is_err(),
166            "Configuration should be invalid when snapshots is enabled but URL is missing"
167        );
168    }
169}