Skip to main content

use_db_schema/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4//! Schema metadata primitives for `RustUse`.
5
6use use_db_name::{CollectionName, ConstraintName, DatabaseName, IndexName, SchemaName, TableName};
7
8/// A database schema reference.
9#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
10pub struct SchemaRef {
11    database: Option<DatabaseName>,
12    schema: SchemaName,
13}
14
15impl SchemaRef {
16    /// Creates a schema reference.
17    #[must_use]
18    pub const fn new(schema: SchemaName) -> Self {
19        Self {
20            database: None,
21            schema,
22        }
23    }
24
25    /// Adds a database/catalog qualifier.
26    #[must_use]
27    pub fn with_database(mut self, database: DatabaseName) -> Self {
28        self.database = Some(database);
29        self
30    }
31
32    /// Returns the optional database/catalog name.
33    #[must_use]
34    pub const fn database(&self) -> Option<&DatabaseName> {
35        self.database.as_ref()
36    }
37
38    /// Returns the schema name.
39    #[must_use]
40    pub const fn schema(&self) -> &SchemaName {
41        &self.schema
42    }
43}
44
45/// Objects that may be described as members of a schema-like namespace.
46#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
47pub enum SchemaObject {
48    /// A table object.
49    Table(TableName),
50    /// A collection object.
51    Collection(CollectionName),
52    /// An index object.
53    Index(IndexName),
54    /// A constraint object.
55    Constraint(ConstraintName),
56    /// An engine-neutral label for another object kind.
57    Other(String),
58}
59
60/// A schema version label.
61#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
62pub struct SchemaVersion(String);
63
64impl SchemaVersion {
65    /// Creates a schema version label.
66    #[must_use]
67    pub fn new(version: impl Into<String>) -> Self {
68        Self(version.into())
69    }
70
71    /// Returns the version label.
72    #[must_use]
73    pub fn as_str(&self) -> &str {
74        &self.0
75    }
76}
77
78/// A schema namespace search list.
79#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
80pub struct SchemaNamespace {
81    schemas: Vec<SchemaName>,
82}
83
84impl SchemaNamespace {
85    /// Creates a schema namespace from ordered schema names.
86    #[must_use]
87    pub const fn new(schemas: Vec<SchemaName>) -> Self {
88        Self { schemas }
89    }
90
91    /// Returns the schema list.
92    #[must_use]
93    pub fn schemas(&self) -> &[SchemaName] {
94        &self.schemas
95    }
96
97    /// Returns whether the namespace is empty.
98    #[must_use]
99    pub fn is_empty(&self) -> bool {
100        self.schemas.is_empty()
101    }
102}
103
104/// Schema metadata.
105#[derive(Clone, Debug, Eq, PartialEq)]
106pub struct SchemaMetadata {
107    reference: SchemaRef,
108    version: Option<SchemaVersion>,
109    objects: Vec<SchemaObject>,
110}
111
112impl SchemaMetadata {
113    /// Creates schema metadata.
114    #[must_use]
115    pub const fn new(reference: SchemaRef) -> Self {
116        Self {
117            reference,
118            version: None,
119            objects: Vec::new(),
120        }
121    }
122
123    /// Adds a schema version label.
124    #[must_use]
125    pub fn with_version(mut self, version: SchemaVersion) -> Self {
126        self.version = Some(version);
127        self
128    }
129
130    /// Adds schema objects.
131    #[must_use]
132    pub fn with_objects(mut self, objects: Vec<SchemaObject>) -> Self {
133        self.objects = objects;
134        self
135    }
136
137    /// Returns the schema reference.
138    #[must_use]
139    pub const fn reference(&self) -> &SchemaRef {
140        &self.reference
141    }
142
143    /// Returns the optional schema version.
144    #[must_use]
145    pub const fn version(&self) -> Option<&SchemaVersion> {
146        self.version.as_ref()
147    }
148
149    /// Returns the schema object list.
150    #[must_use]
151    pub fn objects(&self) -> &[SchemaObject] {
152        &self.objects
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::{SchemaMetadata, SchemaNamespace, SchemaObject, SchemaRef, SchemaVersion};
159    use use_db_name::{DatabaseName, SchemaName, TableName};
160
161    #[test]
162    fn stores_schema_metadata() -> Result<(), Box<dyn std::error::Error>> {
163        let schema = SchemaName::new("public")?;
164        let reference = SchemaRef::new(schema.clone()).with_database(DatabaseName::new("app")?);
165        let metadata = SchemaMetadata::new(reference).with_version(SchemaVersion::new("1"));
166        let namespace = SchemaNamespace::new(vec![schema]);
167
168        assert_eq!(
169            metadata.reference().database().expect("database").as_str(),
170            "app"
171        );
172        assert_eq!(metadata.version().expect("version").as_str(), "1");
173        assert!(!namespace.is_empty());
174        assert_eq!(
175            SchemaObject::Table(TableName::new("users")?),
176            SchemaObject::Table(TableName::new("users")?)
177        );
178        Ok(())
179    }
180}