Heavily customized version of smokesignal - https://whtwnd.com/kayrozen.com/3lpwe4ymowg2t
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 307 lines 12 kB view raw
1//! # Nominatim Client Usage Example 2//! 3//! This example demonstrates how to integrate the new NominatimClient 4//! with existing event storage workflows while maintaining lexicon compatibility. 5 6use anyhow::Result; 7use smokesignal::services::nominatim_client::{NominatimClient, NominatimSearchResult}; 8use smokesignal::storage::cache::create_cache_pool; 9use smokesignal::atproto::lexicon::community::lexicon::location::{Address, Geo}; 10use smokesignal::atproto::lexicon::community::lexicon::calendar::event::EventLocation; 11 12/// Example: Replace deprecated GeocodingService with NominatimClient 13/// 14/// This function shows how to migrate from the old geocoding service 15/// to the new Nominatim client while maintaining backward compatibility. 16pub async fn migrate_from_geocoding_service() -> Result<()> { 17 // Initialize Redis connection pool 18 let redis_url = std::env::var("REDIS_URL") 19 .unwrap_or_else(|_| "redis://valkey:6379/0".to_string()); 20 let redis_pool = create_cache_pool(&redis_url)?; 21 22 // Initialize Nominatim client 23 let nominatim_url = "http://nominatim-quebec:8080".to_string(); 24 let client = NominatimClient::new(redis_pool, nominatim_url)?; 25 26 // Example 1: Basic address geocoding 27 println!("=== Example 1: Basic Address Geocoding ==="); 28 let address = "1000 Rue Saint-Antoine Ouest, Montréal, QC"; 29 30 match client.search_address(address).await { 31 Ok(result) => { 32 println!("✅ Geocoded address: {}", address); 33 print_lexicon_compatible_result(&result); 34 } 35 Err(e) => { 36 println!("❌ Failed to geocode address: {}", e); 37 } 38 } 39 40 // Example 2: Reverse geocoding 41 println!("\n=== Example 2: Reverse Geocoding ==="); 42 let montreal_lat = 45.5017; 43 let montreal_lon = -73.5673; 44 45 match client.reverse_geocode(montreal_lat, montreal_lon).await { 46 Ok(result) => { 47 println!("✅ Reverse geocoded coordinates: {}, {}", montreal_lat, montreal_lon); 48 print_lexicon_compatible_result(&result); 49 } 50 Err(e) => { 51 println!("❌ Failed to reverse geocode: {}", e); 52 } 53 } 54 55 // Example 3: Enhanced venue metadata 56 println!("\n=== Example 3: Enhanced Venue Metadata ==="); 57 if let Ok(result) = client.search_address("Université de Montréal, Montréal, QC").await { 58 // Extract coordinates using pattern matching 59 let Geo::Current { latitude, longitude, .. } = &result.geo; 60 let lat: f64 = latitude.parse()?; 61 let lon: f64 = longitude.parse()?; 62 63 if let Some(venue_metadata) = client.get_venue_metadata(lat, lon).await { 64 println!("✅ Enhanced venue metadata retrieved:"); 65 println!(" Place ID: {:?}", venue_metadata.place_id); 66 println!(" Category: {:?}", venue_metadata.category); 67 println!(" Venue Type: {:?}", venue_metadata.venue_type); 68 println!(" Importance: {:?}", venue_metadata.importance); 69 println!(" Bilingual Names:"); 70 println!(" English: {:?}", venue_metadata.bilingual_names.english); 71 println!(" French: {:?}", venue_metadata.bilingual_names.french); 72 println!(" Display: {}", venue_metadata.bilingual_names.display_name); 73 println!(" Cached at: {}", venue_metadata.cached_at); 74 } 75 } 76 77 Ok(()) 78} 79 80/// Helper function to display lexicon-compatible results 81fn print_lexicon_compatible_result(result: &NominatimSearchResult) { 82 println!("📍 Lexicon Address:"); 83 match &result.address { 84 Address::Current { 85 country, 86 postal_code, 87 region, 88 locality, 89 street, 90 name, 91 } => { 92 println!(" Country: {}", country); 93 if let Some(code) = postal_code { 94 println!(" Postal Code: {}", code); 95 } 96 if let Some(reg) = region { 97 println!(" Region: {}", reg); 98 } 99 if let Some(loc) = locality { 100 println!(" Locality: {}", loc); 101 } 102 if let Some(st) = street { 103 println!(" Street: {}", st); 104 } 105 if let Some(n) = name { 106 println!(" Name: {}", n); 107 } 108 } 109 } 110 111 println!("🗺️ Lexicon Geo:"); 112 match &result.geo { 113 Geo::Current { latitude, longitude, name } => { 114 println!(" Latitude: {}", latitude); 115 println!(" Longitude: {}", longitude); 116 if let Some(n) = name { 117 println!(" Name: {}", n); 118 } 119 } 120 } 121} 122 123/// Example: Convert NominatimSearchResult to EventLocation for existing workflows 124pub fn convert_to_event_location(result: &NominatimSearchResult) -> Vec<EventLocation> { 125 vec![ 126 EventLocation::Address(result.address.clone()), 127 EventLocation::Geo(result.geo.clone()), 128 ] 129} 130 131/// Example: Integration with existing event storage workflow 132pub async fn integrate_with_event_storage() -> Result<()> { 133 // Initialize client 134 let redis_url = std::env::var("REDIS_URL") 135 .unwrap_or_else(|_| "redis://valkey:6379/0".to_string()); 136 let redis_pool = create_cache_pool(&redis_url)?; 137 let nominatim_url = "http://nominatim-quebec:8080".to_string(); 138 let client = NominatimClient::new(redis_pool, nominatim_url)?; 139 140 // Example event address to geocode 141 let event_address = "Place des Arts, Montréal, QC"; 142 143 println!("=== Integrating with Event Storage ==="); 144 145 match client.search_address(event_address).await { 146 Ok(result) => { 147 // Convert to EventLocation format for storage 148 let event_locations = convert_to_event_location(&result); 149 150 println!("✅ Created EventLocation objects:"); 151 for (i, location) in event_locations.iter().enumerate() { 152 match location { 153 EventLocation::Address(_) => { 154 println!(" {}: Address location", i + 1); 155 } 156 EventLocation::Geo(_) => { 157 println!(" {}: Geo location", i + 1); 158 } 159 _ => { 160 println!(" {}: Other location type", i + 1); 161 } 162 } 163 } 164 165 // In a real application, you would store these in the database 166 // along with the enhanced venue metadata cached in Redis 167 println!(" 💾 Ready for database storage"); 168 println!(" 🏪 Enhanced venue metadata cached in Redis"); 169 } 170 Err(e) => { 171 println!("❌ Failed to geocode event address: {}", e); 172 } 173 } 174 175 Ok(()) 176} 177 178/// Example: Performance comparison with caching 179pub async fn demonstrate_caching_performance() -> Result<()> { 180 use std::time::Instant; 181 182 // Initialize client 183 let redis_url = std::env::var("REDIS_URL") 184 .unwrap_or_else(|_| "redis://valkey:6379/0".to_string()); 185 let redis_pool = create_cache_pool(&redis_url)?; 186 let nominatim_url = "http://nominatim-quebec:8080".to_string(); 187 let client = NominatimClient::new(redis_pool, nominatim_url)?; 188 189 let test_address = "McGill University, Montreal, QC"; 190 191 println!("=== Caching Performance Demonstration ==="); 192 193 // First request (API call) 194 let start = Instant::now(); 195 let result1 = client.search_address(test_address).await?; 196 let duration1 = start.elapsed(); 197 198 // Second request (cache hit) 199 let start = Instant::now(); 200 let result2 = client.search_address(test_address).await?; 201 let duration2 = start.elapsed(); 202 203 println!("📊 Performance Results:"); 204 println!(" First request (API): {}ms", duration1.as_millis()); 205 println!(" Second request (cache): {}ms", duration2.as_millis()); 206 println!(" Cache speedup: {:.1}x", duration1.as_millis() as f64 / duration2.as_millis() as f64); 207 208 // Verify results are identical 209 assert_eq!(result1.address, result2.address); 210 assert_eq!(result1.geo, result2.geo); 211 println!(" ✅ Results are identical"); 212 213 // Both should meet performance target 214 assert!(duration1.as_millis() < 500); 215 assert!(duration2.as_millis() < 500); 216 println!(" ✅ Both requests meet <500ms target"); 217 218 Ok(()) 219} 220 221#[tokio::main] 222async fn main() -> Result<()> { 223 // Initialize simple tracing for better debugging 224 tracing_subscriber::fmt::init(); 225 226 println!("🚀 Nominatim Client Integration Examples"); 227 println!("==========================================\n"); 228 229 // Run examples 230 migrate_from_geocoding_service().await?; 231 integrate_with_event_storage().await?; 232 demonstrate_caching_performance().await?; 233 234 println!("\n✅ All examples completed successfully!"); 235 println!("🎯 Ready for production use with lexicon compatibility"); 236 237 Ok(()) 238} 239 240#[cfg(test)] 241mod tests { 242 use super::*; 243 244 #[tokio::test] 245 async fn test_convert_to_event_location() -> Result<()> { 246 use smokesignal::services::nominatim_client::{NominatimSearchResult, VenueMetadata, BilingualNames}; 247 248 // Create a mock result 249 let mock_result = NominatimSearchResult { 250 address: Address::Current { 251 country: "Canada".to_string(), 252 postal_code: Some("H3A 0G4".to_string()), 253 region: Some("Québec".to_string()), 254 locality: Some("Montréal".to_string()), 255 street: Some("845 Rue Sherbrooke Ouest".to_string()), 256 name: Some("McGill University".to_string()), 257 }, 258 geo: Geo::Current { 259 latitude: "45.5048".to_string(), 260 longitude: "-73.5772".to_string(), 261 name: Some("McGill University".to_string()), 262 }, 263 venue_metadata: VenueMetadata { 264 place_id: Some(123456), 265 category: Some("amenity".to_string()), 266 venue_type: Some("university".to_string()), 267 importance: Some(0.75), 268 place_rank: Some(30), 269 osm_type: Some("way".to_string()), 270 osm_id: Some(789012), 271 bilingual_names: BilingualNames { 272 english: Some("McGill University".to_string()), 273 french: Some("Université McGill".to_string()), 274 display_name: "McGill University, Montréal, Québec, Canada".to_string(), 275 }, 276 bounding_box: Some([45.5000, 45.5100, -73.5800, -73.5700]), 277 cached_at: chrono::Utc::now(), 278 }, 279 }; 280 281 // Convert to event locations 282 let event_locations = convert_to_event_location(&mock_result); 283 284 // Verify we get both Address and Geo locations 285 assert_eq!(event_locations.len(), 2); 286 287 match &event_locations[0] { 288 EventLocation::Address(addr) => { 289 if let Address::Current { country, .. } = addr { 290 assert_eq!(country, "Canada"); 291 } 292 } 293 _ => panic!("First location should be Address"), 294 } 295 296 match &event_locations[1] { 297 EventLocation::Geo(geo) => { 298 if let Geo::Current { latitude, .. } = geo { 299 assert_eq!(latitude, "45.5048"); 300 } 301 } 302 _ => panic!("Second location should be Geo"), 303 } 304 305 Ok(()) 306 } 307}