schema-sync 1.0.0

Production-grade schema synchronization for multi-tenant databases
Documentation
//! Developer: s4gor
//! Github: https://github.com/s4gor
//!
//! CLI-related types and context
//!
//! This module contains types used by the CLI layer, particularly
//! tenant context and operation modes.

use serde::{Deserialize, Serialize};

/// Tenant context for all operations
///
/// This ensures that every operation is explicitly scoped to a tenant,
/// preventing accidental cross-tenant operations.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TenantContext {
    /// The tenant identifier (e.g., schema name in PostgreSQL)
    pub tenant_id: String,
}

impl TenantContext {
    /// Create a new tenant context
    pub fn new(tenant_id: impl Into<String>) -> Self {
        Self {
            tenant_id: tenant_id.into(),
        }
    }

    /// Get the tenant identifier
    pub fn id(&self) -> &str {
        &self.tenant_id
    }
}

/// Operation mode for schema synchronization
///
/// Different modes allow the same engine to be used for different purposes:
/// - Dry-run: Preview changes without applying
/// - Validation: Check if schemas match (CI mode)
/// - Sync: Actually apply changes
/// - Audit: Read-only inspection
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OperationMode {
    /// Dry-run mode: calculate and return diff without applying
    DryRun,
    /// Validation mode: check if schemas match, exit non-zero if not
    Validation,
    /// Sync mode: apply changes to bring schemas in sync
    Sync,
    /// Audit mode: read-only inspection, no changes allowed
    Audit,
}

/// Output format for diffs and reports
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutputFormat {
    /// Human-readable text output
    Text,
    /// Machine-readable JSON output
    Json,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_tenant_context() {
        let tenant = TenantContext::new("tenant_123");
        assert_eq!(tenant.id(), "tenant_123");
        assert_eq!(tenant.tenant_id, "tenant_123");
    }

    #[test]
    fn test_tenant_context_serialization() {
        let tenant = TenantContext::new("tenant_123");
        let json = serde_json::to_string(&tenant).unwrap();
        let deserialized: TenantContext = serde_json::from_str(&json).unwrap();
        assert_eq!(tenant, deserialized);
    }

    #[test]
    fn test_tenant_context_hash() {
        let tenant1 = TenantContext::new("tenant_123");
        let tenant2 = TenantContext::new("tenant_123");
        let tenant3 = TenantContext::new("tenant_456");

        use std::collections::HashSet;
        let mut set = HashSet::new();
        set.insert(tenant1.clone());
        set.insert(tenant2.clone());
        assert_eq!(set.len(), 1); // Same tenant_id should hash to same value

        set.insert(tenant3);
        assert_eq!(set.len(), 2); // Different tenant_id should be different
    }
}