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 {}