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

refactor: remove deprecated GeocodingService and clean up warnings

- Delete deprecated geocoding.rs module
- Update imports and remove unused code
- Simplify event storage to extract coordinates only
- Fix code quality issues and eliminate all warnings

kayrozen 4f4d2813 26cb3317

+1 -1
src/errors.rs
··· 33 33 }; 34 34 35 35 match extra.split_once(':') { 36 - Some((message, param)) => (error_code.to_string(), param.trim().to_string()), 36 + Some((_message, param)) => (error_code.to_string(), param.trim().to_string()), 37 37 None => (error_code.to_string(), "".to_string()), 38 38 } 39 39 }
-1
src/http/handle_create_event.rs
··· 39 39 use crate::http::middleware_i18n::Language; 40 40 use crate::http::timezones::supported_timezones; 41 41 use crate::http::utils::url_from_aturi; 42 - use crate::services::{NominatimClient, AddressGeocodingStrategies, AddressComponents}; 43 42 use crate::services::nominatim_client::GeoExt; 44 43 use crate::storage::event::event_insert; 45 44
+10 -11
src/http/handle_event_location_venue.rs
··· 4 4 //! while maintaining full compatibility with existing location workflows. 5 5 6 6 use axum::{ 7 - extract::{Path, Query, State}, 7 + extract::{Query, State}, 8 8 http::StatusCode, 9 9 response::{Html, IntoResponse, Json}, 10 10 }; ··· 649 649 )); 650 650 651 651 // Add coordinates if available (extract from Geo enum) 652 - if let crate::atproto::lexicon::community::lexicon::location::Geo::Current { latitude, longitude, .. } = &venue.geo { 653 - html.push_str(&format!( 654 - r#"<input type="hidden" name="latitude" value="{}" hx-swap-oob="true">"#, 655 - latitude 656 - )); 657 - html.push_str(&format!( 658 - r#"<input type="hidden" name="longitude" value="{}" hx-swap-oob="true">"#, 659 - longitude 660 - )); 661 - } 652 + let crate::atproto::lexicon::community::lexicon::location::Geo::Current { latitude, longitude, .. } = &venue.geo; 653 + html.push_str(&format!( 654 + r#"<input type="hidden" name="latitude" value="{}" hx-swap-oob="true">"#, 655 + latitude 656 + )); 657 + html.push_str(&format!( 658 + r#"<input type="hidden" name="longitude" value="{}" hx-swap-oob="true">"#, 659 + longitude 660 + )); 662 661 663 662 // Clear the venue search suggestions 664 663 html.push_str(r#"<div id="venue-suggestions" hx-swap-oob="innerHTML"></div>"#);
+1 -2
src/services/address_geocoding_strategies.rs
··· 18 18 //! ``` 19 19 20 20 use anyhow::Result; 21 - use crate::services::nominatim_client::{NominatimClient, NominatimSearchResult, NominatimError}; 22 - use crate::atproto::lexicon::community::lexicon::location::Address; 21 + use crate::services::nominatim_client::{NominatimClient, NominatimSearchResult}; 23 22 use tracing; 24 23 25 24 /// Address geocoding strategies with progressive fallback
-190
src/services/geocoding.rs
··· 1 - use anyhow::Result; 2 - use serde::{Deserialize, Serialize}; 3 - use reqwest::Client; 4 - use std::time::Duration; 5 - 6 - /// Legacy geocoding result - DEPRECATED 7 - /// Use the new NominatimClient in nominatim_client.rs for new implementations 8 - #[derive(Debug, Clone, Serialize, Deserialize)] 9 - #[deprecated(note = "Use NominatimClient instead for lexicon-compatible venue data")] 10 - pub struct GeocodingResult { 11 - pub latitude: f64, 12 - pub longitude: f64, 13 - pub display_name: String, 14 - } 15 - 16 - /// Legacy geocoding service - DEPRECATED 17 - /// Use the new NominatimClient in nominatim_client.rs for new implementations 18 - #[deprecated(note = "Use NominatimClient instead for lexicon-compatible venue data")] 19 - pub struct GeocodingService { 20 - client: Client, 21 - } 22 - 23 - impl GeocodingService { 24 - pub fn new() -> Self { 25 - let client = Client::builder() 26 - .timeout(Duration::from_secs(10)) 27 - .user_agent("Smokesignal-Events/1.0") 28 - .build() 29 - .unwrap_or_default(); 30 - 31 - Self { client } 32 - } 33 - 34 - /// Geocode an address string using multiple strategies 35 - pub async fn geocode_address(&self, address: &str) -> Result<GeocodingResult> { 36 - // Strategy 1: Try exact address 37 - if let Ok(result) = self.geocode_exact(address).await { 38 - return Ok(result); 39 - } 40 - 41 - // Strategy 2: Try simplified address (remove building numbers, etc.) 42 - let simplified = self.simplify_address(address); 43 - if simplified != address { 44 - if let Ok(result) = self.geocode_exact(&simplified).await { 45 - return Ok(result); 46 - } 47 - } 48 - 49 - // Strategy 3: Try city/region only 50 - let city_only = self.extract_city_from_address(address); 51 - if !city_only.is_empty() && city_only != address { 52 - if let Ok(result) = self.geocode_exact(&city_only).await { 53 - return Ok(result); 54 - } 55 - } 56 - 57 - Err(anyhow::anyhow!("Unable to geocode address: {}", address)) 58 - } 59 - 60 - /// Perform exact geocoding using Nominatim 61 - async fn geocode_exact(&self, address: &str) -> Result<GeocodingResult> { 62 - let url = format!( 63 - "https://localhost:8080/search?format=json&q={}&limit=1&addressdetails=1&accept-language=fr", 64 - urlencoding::encode(address) 65 - ); 66 - 67 - let response = self.client.get(&url).send().await?; 68 - let data: Vec<NominatimResult> = response.json().await?; 69 - 70 - if let Some(result) = data.first() { 71 - Ok(GeocodingResult { 72 - latitude: result.lat.parse()?, 73 - longitude: result.lon.parse()?, 74 - display_name: result.display_name.clone(), 75 - }) 76 - } else { 77 - Err(anyhow::anyhow!("No results found")) 78 - } 79 - } 80 - 81 - /// Simplify address by removing only very specific details, preserving most address components 82 - fn simplify_address(&self, address: &str) -> String { 83 - let mut simplified = address.to_string(); 84 - 85 - // Remove house/building numbers at the beginning of the address 86 - simplified = regex::Regex::new(r"^\d+(-\d+)?\s+") 87 - .unwrap() 88 - .replace(&simplified, "") 89 - .to_string(); 90 - 91 - // Remove apartment/suite numbers (e.g., "Apt 123", "Suite 456") - these are too specific 92 - // Updated regex to be case-insensitive and handle more variations 93 - simplified = regex::Regex::new(r"(?i),?\s*(apt|apartment|suite|unit|#)\s*\d+\w*") 94 - .unwrap() 95 - .replace_all(&simplified, "") 96 - .to_string(); 97 - 98 - // Remove postal codes (various formats) - often too specific for initial geocoding 99 - simplified = regex::Regex::new(r"\b[A-Z]\d[A-Z]\s*\d[A-Z]\d\b") 100 - .unwrap() 101 - .replace_all(&simplified, "") 102 - .to_string(); // Canadian postal codes 103 - simplified = regex::Regex::new(r"\b\d{5}(-\d{4})?\b") 104 - .unwrap() 105 - .replace_all(&simplified, "") 106 - .to_string(); // US ZIP codes 107 - 108 - // Clean up extra whitespace and commas 109 - simplified = regex::Regex::new(r"\s*,\s*,\s*") 110 - .unwrap() 111 - .replace_all(&simplified, ", ") 112 - .to_string(); 113 - simplified = regex::Regex::new(r"\s+") 114 - .unwrap() 115 - .replace_all(&simplified, " ") 116 - .trim() 117 - .to_string(); 118 - 119 - simplified 120 - } 121 - 122 - /// Extract city/region from address for fallback geocoding 123 - fn extract_city_from_address(&self, address: &str) -> String { 124 - // Look for common patterns to extract city/province 125 - let parts: Vec<&str> = address.split(',').collect(); 126 - 127 - if parts.len() >= 2 { 128 - // Strategy: Skip the first part (likely street address) and take city/region/country components 129 - // For "123 Main St, Montreal, Quebec, Canada" -> take last 3: "Montreal, Quebec, Canada" 130 - // For "Complex Address, Montreal, QC" -> take last 2: "Montreal, QC" 131 - 132 - let remaining_parts: Vec<&str> = parts.iter() 133 - .skip(1) // Always skip the first part (street address) 134 - .map(|s| s.trim()) 135 - .filter(|s| !s.is_empty()) 136 - .collect(); 137 - 138 - remaining_parts.join(", ") 139 - } else { 140 - // Fallback: try to extract just words (remove numbers and special chars) 141 - regex::Regex::new(r"[^\w\s,]") 142 - .unwrap() 143 - .replace_all(address, "") 144 - .trim() 145 - .to_string() 146 - } 147 - } 148 - } 149 - 150 - #[derive(Debug, Deserialize)] 151 - struct NominatimResult { 152 - lat: String, 153 - lon: String, 154 - display_name: String, 155 - } 156 - 157 - #[cfg(test)] 158 - mod tests { 159 - use super::*; 160 - 161 - #[test] 162 - fn test_simplify_address() { 163 - let service = GeocodingService::new(); 164 - 165 - assert_eq!( 166 - service.simplify_address("123 Main St, Apt 456, Montreal, QC H1A 1A1"), 167 - "Main St, Montreal, QC" 168 - ); 169 - 170 - assert_eq!( 171 - service.simplify_address("1000-1010 Sherbrooke St, Montreal, QC"), 172 - "Sherbrooke St, Montreal, QC" 173 - ); 174 - } 175 - 176 - #[test] 177 - fn test_extract_city() { 178 - let service = GeocodingService::new(); 179 - 180 - assert_eq!( 181 - service.extract_city_from_address("123 Main St, Montreal, Quebec, Canada"), 182 - "Montreal, Quebec, Canada" 183 - ); 184 - 185 - assert_eq!( 186 - service.extract_city_from_address("Complex Address, Montreal, QC"), 187 - "Montreal, QC" 188 - ); 189 - } 190 - }
+1 -3
src/services/mod.rs
··· 1 - pub mod geocoding; 2 1 pub mod nominatim_client; 3 2 pub mod address_geocoding_strategies; 4 3 pub mod venues; ··· 7 6 #[cfg(test)] 8 7 mod nominatim_client_tests; 9 8 10 - pub use geocoding::{GeocodingService, GeocodingResult}; 11 9 pub use nominatim_client::{NominatimClient, NominatimSearchResult, VenueMetadata, BilingualNames}; 12 10 pub use address_geocoding_strategies::{AddressGeocodingStrategies, AddressComponents, GeocodingStrategyResult}; 13 11 pub use venues::{ 14 12 VenueSearchService, VenueSearchRequest, VenueSearchResponse, VenueNearbyRequest, 15 - VenueSearchResult, VenueDetails, VenueCategory, SearchRadius, handle_venue_search, 13 + VenueSearchResult, VenueDetails, VenueCategory, handle_venue_search, 16 14 handle_venue_nearby, handle_venue_enrich, handle_venue_suggest 17 15 }; 18 16 pub use events::{EventVenueIntegrationService, VenueIntegrationError};
+6
src/services/nominatim_client.rs
··· 93 93 #[derive(Debug, Deserialize)] 94 94 struct NominatimApiResponse { 95 95 place_id: Option<u64>, 96 + #[allow(dead_code)] 96 97 licence: Option<String>, 97 98 osm_type: Option<String>, 98 99 osm_id: Option<u64>, ··· 104 105 type_: Option<String>, 105 106 place_rank: Option<u32>, 106 107 importance: Option<f64>, 108 + #[allow(dead_code)] 107 109 addresstype: Option<String>, 108 110 name: Option<String>, 109 111 display_name: String, ··· 116 118 struct NominatimAddress { 117 119 house_number: Option<String>, 118 120 road: Option<String>, 121 + #[allow(dead_code)] 119 122 neighbourhood: Option<String>, 123 + #[allow(dead_code)] 120 124 suburb: Option<String>, 121 125 city: Option<String>, 122 126 town: Option<String>, 123 127 village: Option<String>, 124 128 municipality: Option<String>, 129 + #[allow(dead_code)] 125 130 county: Option<String>, 126 131 state_district: Option<String>, 127 132 state: Option<String>, 128 133 region: Option<String>, 129 134 postcode: Option<String>, 130 135 country: Option<String>, 136 + #[allow(dead_code)] 131 137 country_code: Option<String>, 132 138 } 133 139
-1
src/services/venues/venue_cache.rs
··· 18 18 /// Cache TTL constants 19 19 const VENUE_CACHE_TTL_SECS: u64 = 604800; // 7 days for venue enhancement data 20 20 const SEARCH_CACHE_TTL_SECS: u64 = 86400; // 24 hours for search results 21 - const SUGGESTION_CACHE_TTL_SECS: u64 = 43200; // 12 hours for autocomplete suggestions 22 21 23 22 /// Cache key prefixes for different data types 24 23 const CACHE_PREFIX_VENUE_ENHANCEMENT: &str = "venue:enhanced";
-9
src/services/venues/venue_endpoints.rs
··· 118 118 limit: Option<usize>, 119 119 } 120 120 121 - /// Path parameters for venue enrichment endpoint 122 - #[derive(Debug, Deserialize)] 123 - pub struct VenueEnrichParams { 124 - /// Latitude for venue enhancement lookup 125 - lat: f64, 126 - /// Longitude for venue enhancement lookup 127 - lng: f64, 128 - } 129 - 130 121 /// GET /api/venues/search - Text-based venue search 131 122 pub async fn handle_venue_search( 132 123 State(web_context): State<WebContext>,
+2 -9
src/services/venues/venue_search.rs
··· 14 14 use crate::services::nominatim_client::{NominatimClient, NominatimSearchResult}; 15 15 use super::venue_types::{ 16 16 VenueSearchRequest, VenueNearbyRequest, VenueSearchResponse, VenueSearchResult, 17 - VenueDetails, VenueQuality, SearchRadius 17 + VenueDetails, VenueQuality 18 18 }; 19 19 use super::venue_cache::{VenueCacheManager, VenueCacheError}; 20 20 ··· 506 506 } 507 507 } 508 508 509 - #[tokio::test] 510 - async fn test_search_radius_validation() { 511 - let valid_radius = SearchRadius::new(1000); 512 - assert!(valid_radius.is_ok()); 513 - 514 - let invalid_radius = SearchRadius::new(50); 515 - assert!(invalid_radius.is_err()); 516 - } 509 + 517 510 518 511 #[test] 519 512 fn test_coordinate_validation() {
+6 -65
src/storage/event.rs
··· 10 10 use crate::atproto::lexicon::community::lexicon::calendar::rsvp::{ 11 11 Rsvp as RsvpLexicon, RsvpStatus as RsvpStatusLexicon, 12 12 }; 13 - use crate::services::GeocodingService; 14 13 15 14 use super::errors::StorageError; 16 15 use super::StoragePool; ··· 325 324 } 326 325 } 327 326 328 - // Helper function to try geocoding an event record if it needs it 327 + // Helper function to extract coordinates from event record without geocoding 328 + // Geocoding should be handled at the API level using the venue services 329 329 async fn try_geocode_event_record<T: serde::Serialize>( 330 330 record: &T, 331 331 ) -> (Option<(f64, f64)>, serde_json::Value) { 332 332 // Try to parse the event record 333 - if let Ok(mut event_record) = serde_json::from_str::<EventLexicon>(&serde_json::to_string(record).unwrap_or_default()) { 334 - match &mut event_record { 333 + if let Ok(event_record) = serde_json::from_str::<EventLexicon>(&serde_json::to_string(record).unwrap_or_default()) { 334 + match &event_record { 335 335 EventLexicon::Current { locations, .. } => { 336 - // First, try to extract existing coordinates 336 + // Extract existing coordinates only - no geocoding performed 337 337 let existing_coords = extract_coordinates_from_locations(locations); 338 - 339 - if existing_coords.is_some() { 340 - // Already has coordinates, use as-is 341 - (existing_coords, json!(record)) 342 - } else { 343 - // No coordinates found, try to geocode address-only locations 344 - let mut updated_locations = locations.clone(); 345 - let mut found_coordinates = None; 346 - 347 - // Look for address-only locations to geocode 348 - for location in locations.iter() { 349 - if let crate::atproto::lexicon::community::lexicon::calendar::event::EventLocation::Address(address) = location { 350 - // Format the address for geocoding 351 - let address_string = format_address(address); 352 - if !address_string.trim().is_empty() { 353 - // Attempt to geocode the address 354 - let geocoding_service = GeocodingService::new(); 355 - if let Ok(geocoding_result) = geocoding_service.geocode_address(&address_string).await { 356 - // Create a Geo location with the coordinates 357 - let geo_location = crate::atproto::lexicon::community::lexicon::location::Geo::Current { 358 - latitude: geocoding_result.latitude.to_string(), 359 - longitude: geocoding_result.longitude.to_string(), 360 - name: Some(geocoding_result.display_name), 361 - }; 362 - 363 - // Add the geocoded location to the locations list 364 - updated_locations.push(crate::atproto::lexicon::community::lexicon::calendar::event::EventLocation::Geo(geo_location)); 365 - 366 - // Use these coordinates for spatial indexing 367 - found_coordinates = Some((geocoding_result.latitude, geocoding_result.longitude)); 368 - break; // Only geocode the first address found 369 - } 370 - } 371 - } 372 - } 373 - 374 - // Update the event record with the new locations if we geocoded something 375 - if found_coordinates.is_some() { 376 - let updated_event = match event_record { 377 - EventLexicon::Current { name, description, created_at, starts_at, ends_at, mode, status, uris, extra, .. } => { 378 - EventLexicon::Current { 379 - name, 380 - description, 381 - created_at, 382 - starts_at, 383 - ends_at, 384 - mode, 385 - status, 386 - locations: updated_locations, 387 - uris, 388 - extra, 389 - } 390 - } 391 - }; 392 - (found_coordinates, json!(updated_event)) 393 - } else { 394 - // No geocoding performed, use original record 395 - (None, json!(record)) 396 - } 397 - } 338 + (existing_coords, json!(record)) 398 339 } 399 340 } 400 341 } else {