Skip to main content

use_db_driver/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4//! Driver and backend metadata primitives for `RustUse`.
5
6use core::fmt;
7use std::error::Error;
8
9pub use use_db_name::DriverName;
10
11macro_rules! driver_text_type {
12    ($type_name:ident) => {
13        #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
14        pub struct $type_name(String);
15
16        impl $type_name {
17            /// Creates a driver/backend metadata label.
18            ///
19            /// # Errors
20            ///
21            /// Returns [`DriverError`] when the label is empty or contains control characters.
22            pub fn new(input: impl AsRef<str>) -> Result<Self, DriverError> {
23                validate_text(input.as_ref()).map(|value| Self(value.to_owned()))
24            }
25
26            /// Returns the stored label.
27            #[must_use]
28            pub fn as_str(&self) -> &str {
29                &self.0
30            }
31        }
32
33        impl fmt::Display for $type_name {
34            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
35                formatter.write_str(self.as_str())
36            }
37        }
38    };
39}
40
41driver_text_type!(DriverVersion);
42driver_text_type!(BackendName);
43driver_text_type!(BackendFeature);
44
45/// Driver capability metadata.
46#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
47pub enum DriverCapability {
48    /// Connection capability.
49    #[default]
50    Connect,
51    /// Transaction capability.
52    Transactions,
53    /// Pooling capability.
54    Pooling,
55    /// Migration capability.
56    Migrations,
57    /// Other or unspecified capability.
58    Other,
59}
60
61/// Backend kind metadata.
62#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
63pub enum BackendKind {
64    /// SQL backend.
65    #[default]
66    Sql,
67    /// Document backend.
68    Document,
69    /// Key-value backend.
70    KeyValue,
71    /// Graph backend.
72    Graph,
73    /// Search backend.
74    Search,
75    /// Other or unspecified backend.
76    Other,
77}
78
79/// Error returned by driver/backend metadata constructors.
80#[derive(Clone, Copy, Debug, Eq, PartialEq)]
81pub enum DriverError {
82    /// Label was empty.
83    Empty,
84    /// Label contained a control character.
85    ControlCharacter,
86}
87
88impl fmt::Display for DriverError {
89    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
90        match self {
91            Self::Empty => formatter.write_str("driver label cannot be empty"),
92            Self::ControlCharacter => {
93                formatter.write_str("driver label cannot contain control characters")
94            },
95        }
96    }
97}
98
99impl Error for DriverError {}
100
101fn validate_text(input: &str) -> Result<&str, DriverError> {
102    if input.chars().any(char::is_control) {
103        return Err(DriverError::ControlCharacter);
104    }
105    let trimmed = input.trim();
106    if trimmed.is_empty() {
107        return Err(DriverError::Empty);
108    }
109    Ok(trimmed)
110}
111
112#[cfg(test)]
113mod tests {
114    use super::{
115        BackendFeature, BackendKind, BackendName, DriverCapability, DriverName, DriverVersion,
116    };
117
118    #[test]
119    fn stores_driver_metadata() -> Result<(), Box<dyn std::error::Error>> {
120        let driver = DriverName::new("postgres")?;
121        let backend = BackendName::new("postgresql")?;
122        let version = DriverVersion::new("1.0")?;
123        let feature = BackendFeature::new("json")?;
124
125        assert_eq!(driver.as_str(), "postgres");
126        assert_eq!(backend.as_str(), "postgresql");
127        assert_eq!(version.as_str(), "1.0");
128        assert_eq!(feature.as_str(), "json");
129        assert_eq!(DriverCapability::default(), DriverCapability::Connect);
130        assert_eq!(BackendKind::default(), BackendKind::Sql);
131        Ok(())
132    }
133}