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 )
85 }
86
87 /// Create a new synchronous PostgreSQL URI register with custom cache strategy and TLS
88 ///
89 /// # Arguments
90 ///
91 /// * `database_url` - PostgreSQL connection string
92 /// * `table_name` - Name of the database table
93 /// * `max_connections` - Maximum number of connections in the pool
94 /// * `cache_size` - Number of URI-to-ID mappings to cache
95 /// * `cache_strategy` - Optional cache strategy (defaults to Moka if None)
96 /// * `use_tls` - Optional TLS flag (defaults to false/None for backwards compatibility)
97 pub fn new_with_cache_strategy(
98 database_url: &str,
99 table_name: &str,
100 max_connections: u32,
101 cache_size: usize,
102 cache_strategy: Option<CacheStrategy>,
103 use_tls: Option<bool>,
104 ) -> Result<Self> {
105 let runtime = Runtime::new().map_err(|e| {
106 crate::error::Error::Configuration(crate::error::ConfigurationError::InvalidBackoff(
107 format!("Failed to create Tokio runtime: {}", e),
108 ))
109 })?;
110
111 let inner = runtime.block_on(PostgresUriRegister::new_with_cache_strategy(
112 database_url,
113 table_name,
114 max_connections,
115 cache_size,
116 cache_strategy,
117 use_tls,
118 ))?;
119
120 Ok(Self { inner, runtime })
121 }
122
123 /// Register a single URI and return its ID (blocking)
124 ///
125 /// If the URI already exists, returns the existing ID.
126 /// If the URI is new, creates a new ID and returns it.
127 pub fn register_uri(&self, uri: &str) -> Result<u64> {
128 self.runtime.block_on(self.inner.register_uri(uri))
129 }
130
131 /// Register multiple URIs in batch and return their IDs (blocking)
132 ///
133 /// The returned vector maintains order correspondence with the input.
134 pub fn register_uri_batch(&self, uris: &[String]) -> Result<Vec<u64>> {
135 self.runtime.block_on(self.inner.register_uri_batch(uris))
136 }
137
138 /// Register multiple URIs in batch and return a HashMap (blocking)
139 pub fn register_uri_batch_hashmap(&self, uris: &[String]) -> Result<HashMap<String, u64>> {
140 self.runtime
141 .block_on(self.inner.register_uri_batch_hashmap(uris))
142 }
143
144 /// Get statistics about the register (blocking)
145 pub fn stats(&self) -> Result<RegisterStats> {
146 self.runtime.block_on(self.inner.stats())
147 }
148}
149
150// Implement Send + Sync since Runtime is Send + Sync and PostgresUriRegister is Send + Sync
151unsafe impl Send for SyncPostgresUriRegister {}
152unsafe impl Sync for SyncPostgresUriRegister {}