use-db-schema 0.1.0

Primitive database schema metadata for RustUse
Documentation
#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]

//! Schema metadata primitives for `RustUse`.

use use_db_name::{CollectionName, ConstraintName, DatabaseName, IndexName, SchemaName, TableName};

/// A database schema reference.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SchemaRef {
    database: Option<DatabaseName>,
    schema: SchemaName,
}

impl SchemaRef {
    /// Creates a schema reference.
    #[must_use]
    pub const fn new(schema: SchemaName) -> Self {
        Self {
            database: None,
            schema,
        }
    }

    /// Adds a database/catalog qualifier.
    #[must_use]
    pub fn with_database(mut self, database: DatabaseName) -> Self {
        self.database = Some(database);
        self
    }

    /// Returns the optional database/catalog name.
    #[must_use]
    pub const fn database(&self) -> Option<&DatabaseName> {
        self.database.as_ref()
    }

    /// Returns the schema name.
    #[must_use]
    pub const fn schema(&self) -> &SchemaName {
        &self.schema
    }
}

/// Objects that may be described as members of a schema-like namespace.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum SchemaObject {
    /// A table object.
    Table(TableName),
    /// A collection object.
    Collection(CollectionName),
    /// An index object.
    Index(IndexName),
    /// A constraint object.
    Constraint(ConstraintName),
    /// An engine-neutral label for another object kind.
    Other(String),
}

/// A schema version label.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SchemaVersion(String);

impl SchemaVersion {
    /// Creates a schema version label.
    #[must_use]
    pub fn new(version: impl Into<String>) -> Self {
        Self(version.into())
    }

    /// Returns the version label.
    #[must_use]
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

/// A schema namespace search list.
#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SchemaNamespace {
    schemas: Vec<SchemaName>,
}

impl SchemaNamespace {
    /// Creates a schema namespace from ordered schema names.
    #[must_use]
    pub const fn new(schemas: Vec<SchemaName>) -> Self {
        Self { schemas }
    }

    /// Returns the schema list.
    #[must_use]
    pub fn schemas(&self) -> &[SchemaName] {
        &self.schemas
    }

    /// Returns whether the namespace is empty.
    #[must_use]
    pub fn is_empty(&self) -> bool {
        self.schemas.is_empty()
    }
}

/// Schema metadata.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SchemaMetadata {
    reference: SchemaRef,
    version: Option<SchemaVersion>,
    objects: Vec<SchemaObject>,
}

impl SchemaMetadata {
    /// Creates schema metadata.
    #[must_use]
    pub const fn new(reference: SchemaRef) -> Self {
        Self {
            reference,
            version: None,
            objects: Vec::new(),
        }
    }

    /// Adds a schema version label.
    #[must_use]
    pub fn with_version(mut self, version: SchemaVersion) -> Self {
        self.version = Some(version);
        self
    }

    /// Adds schema objects.
    #[must_use]
    pub fn with_objects(mut self, objects: Vec<SchemaObject>) -> Self {
        self.objects = objects;
        self
    }

    /// Returns the schema reference.
    #[must_use]
    pub const fn reference(&self) -> &SchemaRef {
        &self.reference
    }

    /// Returns the optional schema version.
    #[must_use]
    pub const fn version(&self) -> Option<&SchemaVersion> {
        self.version.as_ref()
    }

    /// Returns the schema object list.
    #[must_use]
    pub fn objects(&self) -> &[SchemaObject] {
        &self.objects
    }
}

#[cfg(test)]
mod tests {
    use super::{SchemaMetadata, SchemaNamespace, SchemaObject, SchemaRef, SchemaVersion};
    use use_db_name::{DatabaseName, SchemaName, TableName};

    #[test]
    fn stores_schema_metadata() -> Result<(), Box<dyn std::error::Error>> {
        let schema = SchemaName::new("public")?;
        let reference = SchemaRef::new(schema.clone()).with_database(DatabaseName::new("app")?);
        let metadata = SchemaMetadata::new(reference).with_version(SchemaVersion::new("1"));
        let namespace = SchemaNamespace::new(vec![schema]);

        assert_eq!(
            metadata.reference().database().expect("database").as_str(),
            "app"
        );
        assert_eq!(metadata.version().expect("version").as_str(), "1");
        assert!(!namespace.is_empty());
        assert_eq!(
            SchemaObject::Table(TableName::new("users")?),
            SchemaObject::Table(TableName::new("users")?)
        );
        Ok(())
    }
}