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}