Skip to main content

uri_register/
sync.rs

1// Copyright TELICENT LTD
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Synchronous wrapper for URI register operations
16//!
17//! This module provides a synchronous API for applications that cannot use async/await.
18//! It wraps the async implementation with a lightweight Tokio runtime.
19
20use crate::cache::CacheStrategy;
21use crate::error::Result;
22use crate::postgres::{PostgresUriRegister, RegisterStats};
23use crate::service::UriService;
24use std::collections::HashMap;
25use tokio::runtime::Runtime;
26
27/// Synchronous PostgreSQL URI register
28///
29/// This is a synchronous wrapper around [`PostgresUriRegister`] for use in
30/// synchronous Rust applications. It uses a lightweight current-thread Tokio
31/// runtime internally to execute async operations.
32///
33/// All methods have the same semantics as their async counterparts but block
34/// the calling thread until completion.
35///
36/// # Example
37///
38/// ```rust,no_run
39/// use uri_register::SyncPostgresUriRegister;
40///
41/// fn main() -> uri_register::Result<()> {
42///     let register = SyncPostgresUriRegister::new(
43///         "postgres://localhost/mydb",
44///         "uri_register",
45///         20,
46///         10_000
47///     )?;
48///
49///     let id = register.register_uri("https://example.com")?;
50///     println!("URI registered with ID: {}", id);
51///
52///     Ok(())
53/// }
54/// ```
55pub struct SyncPostgresUriRegister {
56    inner: PostgresUriRegister,
57    runtime: Runtime,
58}
59
60impl SyncPostgresUriRegister {
61    /// Create a new synchronous PostgreSQL URI register with default cache (Moka/W-TinyLFU)
62    ///
63    /// This is the backwards-compatible constructor that uses Moka caching by default.
64    ///
65    /// # Arguments
66    ///
67    /// * `database_url` - PostgreSQL connection string
68    /// * `table_name` - Name of the database table
69    /// * `max_connections` - Maximum number of connections in the pool
70    /// * `cache_size` - Number of URI-to-ID mappings to cache
71    pub fn new(
72        database_url: &str,
73        table_name: &str,
74        max_connections: u32,
75        cache_size: usize,
76    ) -> Result<Self> {
77        Self::new_with_cache_strategy(
78            database_url,
79            table_name,
80            max_connections,
81            cache_size,
82            None, // Default cache strategy
83            None, // Default to no TLS
84            None, // No custom CA cert
85        )
86    }
87
88    /// Create a new synchronous PostgreSQL URI register with custom cache strategy and TLS
89    ///
90    /// # Arguments
91    ///
92    /// * `database_url` - PostgreSQL connection string
93    /// * `table_name` - Name of the database table
94    /// * `max_connections` - Maximum number of connections in the pool
95    /// * `cache_size` - Number of URI-to-ID mappings to cache
96    /// * `cache_strategy` - Optional cache strategy (defaults to Moka if None)
97    /// * `use_tls` - Optional TLS flag (defaults to false/None for backwards compatibility)
98    /// * `ca_cert_path` - Optional path to a PEM-encoded CA certificate file for private CAs
99    pub fn new_with_cache_strategy(
100        database_url: &str,
101        table_name: &str,
102        max_connections: u32,
103        cache_size: usize,
104        cache_strategy: Option<CacheStrategy>,
105        use_tls: Option<bool>,
106        ca_cert_path: Option<&str>,
107    ) -> Result<Self> {
108        let runtime = Runtime::new().map_err(|e| {
109            crate::error::Error::Configuration(crate::error::ConfigurationError::InvalidBackoff(
110                format!("Failed to create Tokio runtime: {}", e),
111            ))
112        })?;
113
114        let inner = runtime.block_on(PostgresUriRegister::new_with_cache_strategy(
115            database_url,
116            table_name,
117            max_connections,
118            cache_size,
119            cache_strategy,
120            use_tls,
121            ca_cert_path,
122        ))?;
123
124        Ok(Self { inner, runtime })
125    }
126
127    /// Register a single URI and return its ID (blocking)
128    ///
129    /// If the URI already exists, returns the existing ID.
130    /// If the URI is new, creates a new ID and returns it.
131    pub fn register_uri(&self, uri: &str) -> Result<u64> {
132        self.runtime.block_on(self.inner.register_uri(uri))
133    }
134
135    /// Register multiple URIs in batch and return their IDs (blocking)
136    ///
137    /// The returned vector maintains order correspondence with the input.
138    pub fn register_uri_batch(&self, uris: &[String]) -> Result<Vec<u64>> {
139        self.runtime.block_on(self.inner.register_uri_batch(uris))
140    }
141
142    /// Register multiple URIs in batch and return a HashMap (blocking)
143    pub fn register_uri_batch_hashmap(&self, uris: &[String]) -> Result<HashMap<String, u64>> {
144        self.runtime
145            .block_on(self.inner.register_uri_batch_hashmap(uris))
146    }
147
148    /// Get statistics about the register (blocking)
149    pub fn stats(&self) -> Result<RegisterStats> {
150        self.runtime.block_on(self.inner.stats())
151    }
152}
153
154// Implement Send + Sync since Runtime is Send + Sync and PostgresUriRegister is Send + Sync
155unsafe impl Send for SyncPostgresUriRegister {}
156unsafe impl Sync for SyncPostgresUriRegister {}