use clap::{Parser, Subcommand};
use colored::*;
use mapradar::client::MapradarClient;
use mapradar::models::{SearchQuery, ServiceType, TravelParameters};
use std::process;
#[derive(Parser)]
#[command(name = "mapradar")]
#[command(about = "CLI for Mapradar Location Intelligence", long_about = None)]
struct Cli {
#[arg(short, long, env = "MAPRADAR_API_KEY")]
api_key: String,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Geocode { address: String },
Reverse { latitude: f64, longitude: f64 },
Nearby {
#[arg(short, long, alias = "addr")]
address: Option<String>,
#[arg(long, alias = "lat")]
latitude: Option<f64>,
#[arg(long, alias = "lng", alias = "lon")]
longitude: Option<f64>,
#[arg(short, long, default_value_t = 1000.0)]
radius: f64,
#[arg(short, long, default_value = "bank")]
r#type: String,
#[arg(short, long, alias = "limit", default_value_t = 10)]
max_results: usize,
},
Distance {
#[arg(long, help = "Origin address")]
origin_addr: Option<String>,
#[arg(long, help = "Origin latitude")]
origin_lat: Option<f64>,
#[arg(long, help = "Origin longitude")]
origin_lng: Option<f64>,
#[arg(long, help = "Destination address")]
dest_addr: Option<String>,
#[arg(long, help = "Destination latitude")]
dest_lat: Option<f64>,
#[arg(long, help = "Destination longitude")]
dest_lng: Option<f64>,
#[arg(
long,
help = "Mode of travel (drive, walk, bicycle, motorcycle, transit)",
default_value = "drive"
)]
mode: String,
},
}
#[tokio::main]
async fn main() {
dotenvy::dotenv().ok();
let cli = Cli::parse();
let client = MapradarClient::new(cli.api_key);
match cli.command {
Commands::Geocode { address } => match client.geocode_async(&address).await {
Ok(loc) => println!("{}", serde_json::to_string_pretty(&loc).unwrap()),
Err(err) => {
eprintln!("{} {}", "Error:".red().bold(), err);
process::exit(1);
}
},
Commands::Reverse {
latitude,
longitude,
} => match client.reverse_geocode_async(latitude, longitude).await {
Ok(address) => println!("{}", serde_json::to_string_pretty(&address).unwrap()),
Err(err) => {
eprintln!("{} {}", "Error:".red().bold(), err);
process::exit(1);
}
},
Commands::Nearby {
address,
latitude,
longitude,
radius,
r#type,
max_results,
} => {
let service_types = r#type
.split(",")
.map(|type_str| match type_str.trim() {
"bank" => ServiceType::Bank,
"hospital" => ServiceType::Hospital,
"school" => ServiceType::School,
"restaurant" => ServiceType::Restaurant,
"bus-stop" => ServiceType::BusStop,
"market" => ServiceType::Market,
"mall" => ServiceType::Mall,
"fuel-station" => ServiceType::FuelStation,
"train-station" => ServiceType::TrainStation,
"taxi-stand" => ServiceType::TaxiStand,
"landmark" => ServiceType::Landmark,
"pharmacy" => ServiceType::Pharmacy,
_ => ServiceType::Landmark, })
.collect::<Vec<ServiceType>>();
let query = if let Some(latitude_val) = latitude {
if let Some(longitude_val) = longitude {
SearchQuery::from_coordinates(latitude_val, longitude_val)
} else {
eprintln!(
"{} Longitude is required when latitude is provided",
"Error:".red().bold()
);
process::exit(1);
}
} else {
if let Some(address_val) = address {
SearchQuery::from_address(address_val)
} else {
eprintln!(
"{} Either address or coordinates must be provided",
"Error:".red().bold()
);
process::exit(1);
}
};
match client
.fetch_intelligence_async(query, service_types, radius / 1000.0, max_results)
.await
{
Ok(intel) => println!("{}", serde_json::to_string_pretty(&intel).unwrap()),
Err(err) => {
eprintln!("{} {}", "Error:".red().bold(), err);
process::exit(1);
}
}
}
Commands::Distance {
origin_addr,
origin_lat,
origin_lng,
dest_addr,
dest_lat,
dest_lng,
mode,
} => {
let api_mode = match mode.to_lowercase().as_str() {
"walk" | "walking" => "WALK",
"bicycle" => "BICYCLE",
"motorcycle" | "bike" | "okada" | "keke" => "TWO_WHEELER",
"transit" | "train" | "bus" | "brt" | "danfo" => "TRANSIT",
_ => "DRIVE",
};
let params = TravelParameters {
origin_latitude: origin_lat,
origin_longitude: origin_lng,
origin_address: origin_addr,
destination_latitude: dest_lat,
destination_longitude: dest_lng,
destination_address: dest_addr,
travel_mode: Some(api_mode.to_string()),
};
match client.calculate_travel_distance_async(params).await {
Ok(dist) => println!("{} {:.2} km", "Distance:".green().bold(), dist),
Err(err) => {
eprintln!("{} {}", "Error:".red().bold(), err);
process::exit(1);
}
}
}
}
}