uri-register 0.3.0

A high-performance PostgreSQL-backed URI dictionary service for assigning unique integer IDs to URIs
Documentation
// Copyright TELICENT LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Synchronous wrapper for URI register operations
//!
//! This module provides a synchronous API for applications that cannot use async/await.
//! It wraps the async implementation with a lightweight Tokio runtime.

use crate::cache::CacheStrategy;
use crate::error::Result;
use crate::postgres::{PostgresUriRegister, RegisterStats};
use crate::service::UriService;
use std::collections::HashMap;
use tokio::runtime::Runtime;

/// Synchronous PostgreSQL URI register
///
/// This is a synchronous wrapper around [`PostgresUriRegister`] for use in
/// synchronous Rust applications. It uses a lightweight current-thread Tokio
/// runtime internally to execute async operations.
///
/// All methods have the same semantics as their async counterparts but block
/// the calling thread until completion.
///
/// # Example
///
/// ```rust,no_run
/// use uri_register::SyncPostgresUriRegister;
///
/// fn main() -> uri_register::Result<()> {
///     let register = SyncPostgresUriRegister::new(
///         "postgres://localhost/mydb",
///         "uri_register",
///         20,
///         10_000
///     )?;
///
///     let id = register.register_uri("https://example.com")?;
///     println!("URI registered with ID: {}", id);
///
///     Ok(())
/// }
/// ```
pub struct SyncPostgresUriRegister {
    inner: PostgresUriRegister,
    runtime: Runtime,
}

impl SyncPostgresUriRegister {
    /// Create a new synchronous PostgreSQL URI register with default cache (Moka/W-TinyLFU)
    ///
    /// This is the backwards-compatible constructor that uses Moka caching by default.
    ///
    /// # Arguments
    ///
    /// * `database_url` - PostgreSQL connection string
    /// * `table_name` - Name of the database table
    /// * `max_connections` - Maximum number of connections in the pool
    /// * `cache_size` - Number of URI-to-ID mappings to cache
    pub fn new(
        database_url: &str,
        table_name: &str,
        max_connections: u32,
        cache_size: usize,
    ) -> Result<Self> {
        Self::new_with_cache_strategy(
            database_url,
            table_name,
            max_connections,
            cache_size,
            None, // Default cache strategy
            None, // Default to no TLS
            None, // No custom CA cert
        )
    }

    /// Create a new synchronous PostgreSQL URI register with custom cache strategy and TLS
    ///
    /// # Arguments
    ///
    /// * `database_url` - PostgreSQL connection string
    /// * `table_name` - Name of the database table
    /// * `max_connections` - Maximum number of connections in the pool
    /// * `cache_size` - Number of URI-to-ID mappings to cache
    /// * `cache_strategy` - Optional cache strategy (defaults to Moka if None)
    /// * `use_tls` - Optional TLS flag (defaults to false/None for backwards compatibility)
    /// * `ca_cert_path` - Optional path to a PEM-encoded CA certificate file for private CAs
    pub fn new_with_cache_strategy(
        database_url: &str,
        table_name: &str,
        max_connections: u32,
        cache_size: usize,
        cache_strategy: Option<CacheStrategy>,
        use_tls: Option<bool>,
        ca_cert_path: Option<&str>,
    ) -> Result<Self> {
        let runtime = Runtime::new().map_err(|e| {
            crate::error::Error::Configuration(crate::error::ConfigurationError::InvalidBackoff(
                format!("Failed to create Tokio runtime: {}", e),
            ))
        })?;

        let inner = runtime.block_on(PostgresUriRegister::new_with_cache_strategy(
            database_url,
            table_name,
            max_connections,
            cache_size,
            cache_strategy,
            use_tls,
            ca_cert_path,
        ))?;

        Ok(Self { inner, runtime })
    }

    /// Register a single URI and return its ID (blocking)
    ///
    /// If the URI already exists, returns the existing ID.
    /// If the URI is new, creates a new ID and returns it.
    pub fn register_uri(&self, uri: &str) -> Result<u64> {
        self.runtime.block_on(self.inner.register_uri(uri))
    }

    /// Register multiple URIs in batch and return their IDs (blocking)
    ///
    /// The returned vector maintains order correspondence with the input.
    pub fn register_uri_batch(&self, uris: &[String]) -> Result<Vec<u64>> {
        self.runtime.block_on(self.inner.register_uri_batch(uris))
    }

    /// Register multiple URIs in batch and return a HashMap (blocking)
    pub fn register_uri_batch_hashmap(&self, uris: &[String]) -> Result<HashMap<String, u64>> {
        self.runtime
            .block_on(self.inner.register_uri_batch_hashmap(uris))
    }

    /// Get statistics about the register (blocking)
    pub fn stats(&self) -> Result<RegisterStats> {
        self.runtime.block_on(self.inner.stats())
    }
}

// Implement Send + Sync since Runtime is Send + Sync and PostgresUriRegister is Send + Sync
unsafe impl Send for SyncPostgresUriRegister {}
unsafe impl Sync for SyncPostgresUriRegister {}