use crate::models::Waypoint;
use crate::models::{Route, UserLocation};
use crate::routing_adapters::error::InstantiationError;
use error::{ParsingError, RoutingRequestGenerationError};
#[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::collections::BTreeMap as HashMap;
#[cfg(feature = "std")]
use std::{collections::HashMap, fmt::Debug};
#[cfg(feature = "wasm-bindgen")]
use serde_json::json;
#[cfg(feature = "wasm-bindgen")]
use tsify::Tsify;
#[cfg(feature = "wasm-bindgen")]
use wasm_bindgen::{JsValue, prelude::wasm_bindgen};
use crate::routing_adapters::graphhopper::GraphHopperVoiceUnits;
use crate::routing_adapters::{
graphhopper::GraphHopperHttpRequestGenerator, osrm::OsrmResponseParser,
valhalla::ValhallaHttpRequestGenerator,
};
#[cfg(feature = "alloc")]
use alloc::{string::String, sync::Arc, vec::Vec};
use serde::{Deserialize, Serialize};
pub mod error;
pub mod graphhopper;
pub mod osrm;
pub mod utilities;
pub mod valhalla;
#[derive(PartialEq, Debug)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
pub enum RouteRequest {
HttpPost {
url: String,
headers: HashMap<String, String>,
body: Vec<u8>,
},
HttpGet {
url: String,
headers: HashMap<String, String>,
},
}
#[cfg_attr(feature = "uniffi", uniffi::export(with_foreign))]
pub trait RouteRequestGenerator: Send + Sync {
fn generate_request(
&self,
user_location: UserLocation,
waypoints: Vec<Waypoint>,
) -> Result<RouteRequest, RoutingRequestGenerationError>;
}
#[cfg_attr(feature = "uniffi", uniffi::export(with_foreign))]
pub trait RouteResponseParser: Send + Sync {
fn parse_response(&self, response: Vec<u8>) -> Result<Vec<Route>, ParsingError>;
}
#[cfg_attr(feature = "uniffi", derive(uniffi::Object))]
pub struct RouteAdapter {
request_generator: Arc<dyn RouteRequestGenerator>,
response_parser: Arc<dyn RouteResponseParser>,
}
#[cfg_attr(feature = "uniffi", uniffi::export)]
impl RouteAdapter {
#[cfg_attr(feature = "uniffi", uniffi::constructor)]
pub fn new(
request_generator: Arc<dyn RouteRequestGenerator>,
response_parser: Arc<dyn RouteResponseParser>,
) -> Self {
Self {
request_generator,
response_parser,
}
}
#[cfg_attr(feature = "uniffi", uniffi::constructor)]
pub fn from_well_known_route_provider(
well_known_route_provider: WellKnownRouteProvider,
) -> Result<Self, InstantiationError> {
let response_parser = match &well_known_route_provider {
WellKnownRouteProvider::Valhalla { .. }
| WellKnownRouteProvider::GraphHopper { .. } => Arc::new(OsrmResponseParser::new(6)),
};
let request_generator = match well_known_route_provider {
WellKnownRouteProvider::Valhalla {
endpoint_url,
profile,
options_json,
} => Arc::new(ValhallaHttpRequestGenerator::with_options_json(
endpoint_url,
profile,
options_json.as_deref(),
)?) as Arc<dyn RouteRequestGenerator>,
WellKnownRouteProvider::GraphHopper {
endpoint_url,
profile,
locale,
voice_units,
options_json,
} => Arc::new(GraphHopperHttpRequestGenerator::with_options_json(
endpoint_url,
profile,
locale,
voice_units,
options_json.as_deref(),
)?) as Arc<dyn RouteRequestGenerator>,
};
Ok(Self::new(request_generator, response_parser))
}
pub fn generate_request(
&self,
user_location: UserLocation,
waypoints: Vec<Waypoint>,
) -> Result<RouteRequest, RoutingRequestGenerationError> {
self.request_generator
.generate_request(user_location, waypoints)
}
pub fn parse_response(&self, response: Vec<u8>) -> Result<Vec<Route>, ParsingError> {
self.response_parser.parse_response(response)
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
#[cfg_attr(feature = "wasm-bindgen", derive(Tsify))]
#[cfg_attr(feature = "wasm-bindgen", tsify(from_wasm_abi))]
pub enum WellKnownRouteProvider {
#[cfg_attr(feature = "wasm-bindgen", serde(rename_all = "camelCase"))]
Valhalla {
endpoint_url: String,
profile: String,
#[serde(default)]
#[cfg_attr(feature = "uniffi", uniffi(default))]
options_json: Option<String>,
},
#[cfg_attr(feature = "wasm-bindgen", serde(rename_all = "camelCase"))]
GraphHopper {
endpoint_url: String,
profile: String,
locale: String,
voice_units: GraphHopperVoiceUnits,
#[serde(default)]
#[cfg_attr(feature = "uniffi", uniffi(default))]
options_json: Option<String>,
},
}
#[cfg(feature = "wasm-bindgen")]
#[wasm_bindgen(js_name = RouteAdapter)]
pub struct JsRouteAdapter(RouteAdapter);
#[cfg(feature = "wasm-bindgen")]
#[wasm_bindgen(js_class = RouteAdapter)]
impl JsRouteAdapter {
#[wasm_bindgen(constructor)]
pub fn new(
well_known_route_provider: WellKnownRouteProvider,
) -> Result<JsRouteAdapter, JsValue> {
RouteAdapter::from_well_known_route_provider(well_known_route_provider)
.map(JsRouteAdapter)
.map_err(|e| JsValue::from_str(&format!("{}", e)))
}
#[wasm_bindgen(js_name = generateRequest)]
pub fn generate_request(
&self,
user_location: JsValue,
waypoints: JsValue,
) -> Result<JsValue, JsValue> {
let user_location: UserLocation = serde_wasm_bindgen::from_value(user_location)
.map_err(|e| JsValue::from_str(&e.to_string()))?;
let waypoints: Vec<Waypoint> = serde_wasm_bindgen::from_value(waypoints)
.map_err(|e| JsValue::from_str(&e.to_string()))?;
match self.0.generate_request(user_location, waypoints) {
Ok(RouteRequest::HttpPost { url, headers, body }) => {
serde_wasm_bindgen::to_value(&json!({
"method": "post",
"url": url,
"headers": headers,
"body": body,
}))
.map_err(|e| JsValue::from_str(&e.to_string()))
}
Ok(RouteRequest::HttpGet { url, headers }) => serde_wasm_bindgen::to_value(&json!({
"method": "get",
"url": url,
"headers": headers,
}))
.map_err(|e| JsValue::from_str(&e.to_string())),
Err(e) => Err(JsValue::from_str(&e.to_string())),
}
}
#[wasm_bindgen(js_name = parseResponse)]
pub fn parse_response(&self, response: Vec<u8>) -> Result<JsValue, JsValue> {
match self.0.parse_response(response.into()) {
Ok(routes) => serde_wasm_bindgen::to_value(&routes).map_err(JsValue::from),
Err(error) => Err(JsValue::from_str(&error.to_string())),
}
}
}