Skip to main content

drasi_bootstrap_postgres/
config.rs

1// Copyright 2025 The Drasi Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Configuration types for the PostgreSQL bootstrap provider.
16//!
17//! These types are defined locally to keep this component independent
18//! and self-contained, without dependencies on other components.
19
20use serde::{Deserialize, Serialize};
21
22// =============================================================================
23// SSL Configuration
24// =============================================================================
25
26/// SSL mode for PostgreSQL connections
27#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
28#[serde(rename_all = "lowercase")]
29pub enum SslMode {
30    /// Disable SSL encryption
31    Disable,
32    /// Prefer SSL but allow unencrypted connections
33    Prefer,
34    /// Require SSL encryption
35    Require,
36}
37
38impl Default for SslMode {
39    fn default() -> Self {
40        Self::Prefer
41    }
42}
43
44impl std::fmt::Display for SslMode {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        match self {
47            Self::Disable => write!(f, "disable"),
48            Self::Prefer => write!(f, "prefer"),
49            Self::Require => write!(f, "require"),
50        }
51    }
52}
53
54// =============================================================================
55// Database Table Configuration
56// =============================================================================
57
58/// Table key configuration for PostgreSQL bootstrap
59#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
60pub struct TableKeyConfig {
61    pub table: String,
62    pub key_columns: Vec<String>,
63}
64
65/// PostgreSQL bootstrap provider configuration
66#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
67pub struct PostgresBootstrapConfig {
68    /// PostgreSQL host
69    #[serde(default = "default_postgres_host")]
70    pub host: String,
71
72    /// PostgreSQL port
73    #[serde(default = "default_postgres_port")]
74    pub port: u16,
75
76    /// Database name
77    pub database: String,
78
79    /// Database user
80    pub user: String,
81
82    /// Database password
83    #[serde(default)]
84    pub password: String,
85
86    /// Tables to bootstrap
87    #[serde(default)]
88    pub tables: Vec<String>,
89
90    /// Replication slot name (for compatibility, not used in bootstrap)
91    #[serde(default = "default_slot_name")]
92    pub slot_name: String,
93
94    /// Publication name (for compatibility, not used in bootstrap)
95    #[serde(default = "default_publication_name")]
96    pub publication_name: String,
97
98    /// SSL mode
99    #[serde(default)]
100    pub ssl_mode: SslMode,
101
102    /// Table key configurations
103    #[serde(default)]
104    pub table_keys: Vec<TableKeyConfig>,
105}
106
107fn default_postgres_host() -> String {
108    "localhost".to_string() // DevSkim: ignore DS162092
109}
110
111fn default_postgres_port() -> u16 {
112    5432
113}
114
115fn default_slot_name() -> String {
116    "drasi_slot".to_string()
117}
118
119fn default_publication_name() -> String {
120    "drasi_publication".to_string()
121}
122
123impl PostgresBootstrapConfig {
124    /// Validate the configuration and return an error if invalid.
125    ///
126    /// # Errors
127    ///
128    /// Returns an error if:
129    /// - Database name is empty
130    /// - User is empty
131    /// - Port is 0
132    pub fn validate(&self) -> anyhow::Result<()> {
133        if self.database.is_empty() {
134            return Err(anyhow::anyhow!(
135                "Validation error: database cannot be empty. \
136                 Please specify the PostgreSQL database name"
137            ));
138        }
139
140        if self.user.is_empty() {
141            return Err(anyhow::anyhow!(
142                "Validation error: user cannot be empty. \
143                 Please specify the PostgreSQL user"
144            ));
145        }
146
147        if self.port == 0 {
148            return Err(anyhow::anyhow!(
149                "Validation error: port cannot be 0. \
150                 Please specify a valid port number (1-65535)"
151            ));
152        }
153
154        Ok(())
155    }
156}