fraiseql-core 2.3.2

Core execution engine for FraiseQL v2 - Compiled GraphQL over SQL
Documentation
//! Thread-safe registry for custom scalar implementations.
//!
//! This module provides a global registry for managing custom scalar implementations
//! at runtime, allowing applications to register their own scalar types.

use std::{
    collections::HashMap,
    sync::{Arc, RwLock},
};

use super::custom_scalar::CustomScalar;

/// Thread-safe registry for custom scalar implementations.
///
/// Uses `Arc<RwLock<HashMap>>` for concurrent read access with exclusive write access.
pub struct CustomScalarRegistry {
    scalars: Arc<RwLock<HashMap<String, Arc<dyn CustomScalar>>>>,
}

impl CustomScalarRegistry {
    /// Create a new custom scalar registry.
    #[must_use]
    pub fn new() -> Self {
        Self {
            scalars: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    /// Register a custom scalar implementation.
    ///
    /// # Arguments
    ///
    /// * `scalar` - The scalar implementation to register
    ///
    /// # Errors
    ///
    /// Returns an error if a scalar with the same name is already registered.
    ///
    /// # Example
    ///
    /// ```
    /// use fraiseql_core::validation::{CustomScalarRegistry, CustomScalar};
    /// use fraiseql_core::error::Result;
    /// use serde_json::Value;
    /// use std::sync::Arc;
    ///
    /// #[derive(Debug)]
    /// struct Email;
    /// impl CustomScalar for Email {
    ///     fn name(&self) -> &str { "Email" }
    ///     fn serialize(&self, v: &Value) -> Result<Value> { Ok(v.clone()) }
    ///     fn parse_value(&self, v: &Value) -> Result<Value> { Ok(v.clone()) }
    ///     fn parse_literal(&self, v: &Value) -> Result<Value> { Ok(v.clone()) }
    /// }
    ///
    /// let registry = CustomScalarRegistry::new();
    /// registry.register(Arc::new(Email)).unwrap();
    /// assert!(registry.has_scalar("Email").unwrap());
    /// ```
    pub fn register(&self, scalar: Arc<dyn CustomScalar>) -> crate::error::Result<()> {
        let name = scalar.name().to_string();

        let mut scalars = self.scalars.write().map_err(|_| {
            crate::error::FraiseQLError::internal("Failed to acquire write lock on scalar registry")
        })?;

        if scalars.contains_key(&name) {
            return Err(crate::error::FraiseQLError::validation(format!(
                "Scalar \"{}\" is already registered",
                name
            )));
        }

        scalars.insert(name, scalar);
        Ok(())
    }

    /// Get a registered scalar by name.
    ///
    /// Returns `None` if the scalar is not registered.
    ///
    /// # Errors
    ///
    /// Returns `FraiseQLError::Internal` if the read lock cannot be acquired.
    pub fn get_scalar(&self, name: &str) -> crate::error::Result<Option<Arc<dyn CustomScalar>>> {
        let scalars = self.scalars.read().map_err(|_| {
            crate::error::FraiseQLError::internal("Failed to acquire read lock on scalar registry")
        })?;

        Ok(scalars.get(name).cloned())
    }

    /// Check if a scalar is registered.
    ///
    /// # Errors
    ///
    /// Returns `FraiseQLError::Internal` if the read lock cannot be acquired.
    pub fn has_scalar(&self, name: &str) -> crate::error::Result<bool> {
        let scalars = self.scalars.read().map_err(|_| {
            crate::error::FraiseQLError::internal("Failed to acquire read lock on scalar registry")
        })?;

        Ok(scalars.contains_key(name))
    }

    /// Get all registered scalar names.
    ///
    /// # Errors
    ///
    /// Returns `FraiseQLError::Internal` if the read lock cannot be acquired.
    pub fn get_scalar_names(&self) -> crate::error::Result<Vec<String>> {
        let scalars = self.scalars.read().map_err(|_| {
            crate::error::FraiseQLError::internal("Failed to acquire read lock on scalar registry")
        })?;

        Ok(scalars.keys().cloned().collect())
    }

    /// Unregister a scalar by name (useful for testing).
    ///
    /// # Errors
    ///
    /// Returns `FraiseQLError::Internal` if the write lock cannot be acquired.
    pub fn unregister(&self, name: &str) -> crate::error::Result<()> {
        let mut scalars = self.scalars.write().map_err(|_| {
            crate::error::FraiseQLError::internal("Failed to acquire write lock on scalar registry")
        })?;

        scalars.remove(name);
        Ok(())
    }

    /// Clear all registered scalars (useful for testing).
    ///
    /// # Errors
    ///
    /// Returns `FraiseQLError::Internal` if the write lock cannot be acquired.
    pub fn clear(&self) -> crate::error::Result<()> {
        let mut scalars = self.scalars.write().map_err(|_| {
            crate::error::FraiseQLError::internal("Failed to acquire write lock on scalar registry")
        })?;

        scalars.clear();
        Ok(())
    }
}

impl Default for CustomScalarRegistry {
    fn default() -> Self {
        Self::new()
    }
}

impl Clone for CustomScalarRegistry {
    fn clone(&self) -> Self {
        Self {
            scalars: Arc::clone(&self.scalars),
        }
    }
}