storage_kit/databases/config/common.rs
1use std::time::Duration;
2
3use serde::{Deserialize, Serialize};
4
5use crate::{CapabilitySet, StorageError, StorageLocator};
6
7/// Shared policy applied to every built-in database connection.
8///
9/// Defaults allow writes, require no optional capabilities, and use a 15-second
10/// connect timeout and a 30-second operation deadline. The operation deadline
11/// includes internal preflight and backend-specific retry work. Mutations are
12/// not automatically replayed after uncertain completion.
13/// Configuration is validated by [`DatabaseConfig::validate`](crate::database::config::DatabaseConfig::validate) or
14/// [`crate::Database::connect`]; merely constructing this value performs no I/O.
15///
16/// # Examples
17///
18/// ```
19/// use storage_kit::database::config::{CommonConfig, DatabaseConfig, LanceConfig};
20/// let common = CommonConfig { request_timeout_ms: 5_000, read_only: true,
21/// ..Default::default() };
22/// let config = DatabaseConfig::lance(LanceConfig::embedded_path("memory://"))
23/// .with_common(common);
24/// config.validate()?;
25/// assert!(config.common().read_only);
26/// # Ok::<(), storage_kit::StorageError>(())
27/// ```
28#[derive(Clone, Debug, Serialize, Deserialize)]
29pub struct CommonConfig {
30 /// Default locator used to fill omitted request scope.
31 pub default_locator: Option<StorageLocator>,
32 /// Connection timeout in milliseconds.
33 pub connect_timeout_ms: u64,
34 /// Portable operation timeout in milliseconds.
35 pub request_timeout_ms: u64,
36 /// Rejects mutating operations when set.
37 pub read_only: bool,
38 /// Optional retained capabilities required at connection time.
39 pub required_capabilities: CapabilitySet,
40}
41
42impl Default for CommonConfig {
43 fn default() -> Self {
44 Self {
45 default_locator: None,
46 connect_timeout_ms: 15_000,
47 request_timeout_ms: 30_000,
48 read_only: false,
49 required_capabilities: CapabilitySet::empty(),
50 }
51 }
52}
53
54impl CommonConfig {
55 pub(crate) fn validate(&self) -> Result<(), StorageError> {
56 if self.connect_timeout_ms == 0 {
57 return Err(StorageError::InvalidConfig(
58 "connect_timeout_ms must be greater than zero".into(),
59 ));
60 }
61 if self.request_timeout_ms == 0 {
62 return Err(StorageError::InvalidConfig(
63 "request_timeout_ms must be greater than zero".into(),
64 ));
65 }
66 if let Some(locator) = &self.default_locator {
67 locator.validate_for_config("default_locator")?;
68 }
69 Ok(())
70 }
71
72 pub(crate) fn connect_timeout(&self) -> Duration {
73 Duration::from_millis(self.connect_timeout_ms)
74 }
75
76 pub(crate) fn request_timeout(&self) -> Duration {
77 Duration::from_millis(self.request_timeout_ms)
78 }
79
80 pub(crate) fn ensure_mutable(&self, operation: &'static str) -> Result<(), StorageError> {
81 if self.read_only {
82 return Err(StorageError::backend_op(
83 operation,
84 format!(
85 "operation `{operation}` is disabled because the database is configured read-only"
86 ),
87 ));
88 }
89 Ok(())
90 }
91}