use reverse_geocoder::ReverseGeocoder;
use rusqlite::Connection;
use std::sync::OnceLock;
pub fn ensure_location_column(conn: &Connection) {
let _ = conn.execute_batch("ALTER TABLE file_hashes ADD COLUMN location_name TEXT");
}
static GEOCODER: OnceLock<ReverseGeocoder> = OnceLock::new();
pub fn geocoder() -> &'static ReverseGeocoder {
GEOCODER.get_or_init(ReverseGeocoder::new)
}
pub fn location_name(lat: f64, lon: f64) -> Option<String> {
let result = geocoder().search((lat, lon));
let record = &result.record;
if record.name.is_empty() {
None
} else {
Some(format!("{}, {}", record.name, record.cc))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ensure_location_column_is_idempotent() {
let conn = Connection::open_in_memory().unwrap();
conn.execute_batch(
"CREATE TABLE file_hashes (path TEXT PRIMARY KEY, hash TEXT NOT NULL);",
)
.unwrap();
ensure_location_column(&conn);
ensure_location_column(&conn); conn.execute(
"UPDATE file_hashes SET location_name = 'Paris, FR' WHERE path = 'x'",
[],
)
.unwrap();
}
#[test]
fn location_name_resolves_known_city() {
let name = location_name(48.8566, 2.3522).unwrap();
assert!(name.contains("FR"), "expected France country code, got: {name}");
}
}