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")]
29#[derive(Default)]
30pub enum SslMode {
31 /// Disable SSL encryption
32 Disable,
33 /// Prefer SSL but allow unencrypted connections
34 #[default]
35 Prefer,
36 /// Require SSL encryption
37 Require,
38}
39
40impl std::fmt::Display for SslMode {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 match self {
43 Self::Disable => write!(f, "disable"),
44 Self::Prefer => write!(f, "prefer"),
45 Self::Require => write!(f, "require"),
46 }
47 }
48}
49
50// =============================================================================
51// Database Table Configuration
52// =============================================================================
53
54/// Table key configuration for PostgreSQL bootstrap
55#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
56pub struct TableKeyConfig {
57 pub table: String,
58 pub key_columns: Vec<String>,
59}
60
61/// PostgreSQL bootstrap provider configuration
62#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
63pub struct PostgresBootstrapConfig {
64 /// PostgreSQL host
65 #[serde(default = "default_postgres_host")]
66 pub host: String,
67
68 /// PostgreSQL port
69 #[serde(default = "default_postgres_port")]
70 pub port: u16,
71
72 /// Database name
73 pub database: String,
74
75 /// Database user
76 pub user: String,
77
78 /// Database password
79 #[serde(default)]
80 pub password: String,
81
82 /// Tables to bootstrap
83 #[serde(default)]
84 pub tables: Vec<String>,
85
86 /// Replication slot name (for compatibility, not used in bootstrap)
87 #[serde(default = "default_slot_name")]
88 pub slot_name: String,
89
90 /// Publication name (for compatibility, not used in bootstrap)
91 #[serde(default = "default_publication_name")]
92 pub publication_name: String,
93
94 /// SSL mode
95 #[serde(default)]
96 pub ssl_mode: SslMode,
97
98 /// Table key configurations
99 #[serde(default)]
100 pub table_keys: Vec<TableKeyConfig>,
101}
102
103fn default_postgres_host() -> String {
104 "localhost".to_string() // DevSkim: ignore DS162092
105}
106
107fn default_postgres_port() -> u16 {
108 5432
109}
110
111fn default_slot_name() -> String {
112 "drasi_slot".to_string()
113}
114
115fn default_publication_name() -> String {
116 "drasi_publication".to_string()
117}
118
119impl PostgresBootstrapConfig {
120 /// Validate the configuration and return an error if invalid.
121 ///
122 /// # Errors
123 ///
124 /// Returns an error if:
125 /// - Database name is empty
126 /// - User is empty
127 /// - Port is 0
128 pub fn validate(&self) -> anyhow::Result<()> {
129 if self.database.is_empty() {
130 return Err(anyhow::anyhow!(
131 "Validation error: database cannot be empty. \
132 Please specify the PostgreSQL database name"
133 ));
134 }
135
136 if self.user.is_empty() {
137 return Err(anyhow::anyhow!(
138 "Validation error: user cannot be empty. \
139 Please specify the PostgreSQL user"
140 ));
141 }
142
143 if self.port == 0 {
144 return Err(anyhow::anyhow!(
145 "Validation error: port cannot be 0. \
146 Please specify a valid port number (1-65535)"
147 ));
148 }
149
150 Ok(())
151 }
152}