storage_kit/databases/config/
surreal.rs1use std::path::{Path, PathBuf};
2
3use serde::{Deserialize, Serialize};
4
5use crate::{Password, StorageError};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
18pub enum SurrealModeConfig {
19 Mem,
21 RocksDb {
23 path: PathBuf,
25 },
26 SurrealKv {
28 path: PathBuf,
30 },
31 RemoteWs {
33 endpoint: String,
35 },
36 RemoteHttp {
38 endpoint: String,
40 },
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct SurrealConfig {
68 pub max_retries: u32,
70 pub mode: SurrealModeConfig,
72 pub namespace: Option<String>,
74 pub database: Option<String>,
76 pub username: Option<String>,
78 #[serde(default, skip_serializing_if = "Option::is_none")]
80 pub password: Option<Password>,
81}
82
83impl SurrealConfig {
84 pub(crate) fn validate(&self) -> Result<(), StorageError> {
85 match &self.mode {
86 SurrealModeConfig::Mem => {}
87 SurrealModeConfig::RocksDb { path } => validate_non_empty_path("mode.path", path)?,
88 SurrealModeConfig::SurrealKv { path } => validate_non_empty_path("mode.path", path)?,
89 SurrealModeConfig::RemoteWs { endpoint } => {
90 validate_endpoint("mode.endpoint", endpoint, &["ws", "wss"])?
91 }
92 SurrealModeConfig::RemoteHttp { endpoint } => {
93 validate_endpoint("mode.endpoint", endpoint, &["http", "https"])?
94 }
95 }
96
97 validate_optional_non_empty("namespace", self.namespace.as_deref())?;
98 validate_optional_non_empty("database", self.database.as_deref())?;
99 validate_optional_non_empty("username", self.username.as_deref())?;
100
101 Ok(())
102 }
103}
104
105impl Default for SurrealConfig {
106 fn default() -> Self {
107 Self {
108 mode: SurrealModeConfig::Mem,
109 max_retries: 2,
110 namespace: Some("test".into()),
111 database: Some("test".into()),
112 username: None,
113 password: None,
114 }
115 }
116}
117
118fn validate_non_empty_path(field: &str, path: &Path) -> Result<(), StorageError> {
119 if path.as_os_str().is_empty() {
120 return Err(StorageError::InvalidConfig(format!(
121 "{field} must not be empty"
122 )));
123 }
124 Ok(())
125}
126
127fn validate_optional_non_empty(field: &str, value: Option<&str>) -> Result<(), StorageError> {
128 if let Some(value) = value
129 && value.trim().is_empty()
130 {
131 return Err(StorageError::InvalidConfig(format!(
132 "{field} must not be empty when configured"
133 )));
134 }
135 Ok(())
136}
137
138fn validate_endpoint(field: &str, endpoint: &str, schemes: &[&str]) -> Result<(), StorageError> {
139 validate_optional_non_empty(field, Some(endpoint))?;
140 let parsed = url::Url::parse(endpoint).map_err(|err| {
141 StorageError::InvalidConfig(format!("{field} must be a valid URL: {err}"))
142 })?;
143 if !schemes.iter().any(|scheme| parsed.scheme() == *scheme) {
144 return Err(StorageError::InvalidConfig(format!(
145 "{field} must use one of the following schemes: {}",
146 schemes.join(", ")
147 )));
148 }
149 Ok(())
150}