use prost::Message;
use crate::{Config, Error, proto, proto::options::Format};
#[allow(clippy::needless_lifetimes)] #[cxx::bridge]
mod ffi {
struct Response {
data: UniquePtr<CxxString>,
format: i32,
}
unsafe extern "C++" {
include!("valhalla/src/actor.hpp");
#[namespace = "boost::property_tree"]
type ptree = crate::config::ffi::ptree;
type Actor;
fn new_actor(config: &ptree) -> Result<UniquePtr<Actor>>;
fn route(self: Pin<&mut Actor>, request: &[u8]) -> Result<Response>;
fn locate(self: Pin<&mut Actor>, request: &[u8]) -> Result<Response>;
fn matrix(self: Pin<&mut Actor>, request: &[u8]) -> Result<Response>;
fn optimized_route(self: Pin<&mut Actor>, request: &[u8]) -> Result<Response>;
fn isochrone(self: Pin<&mut Actor>, request: &[u8]) -> Result<Response>;
fn trace_route(self: Pin<&mut Actor>, request: &[u8]) -> Result<Response>;
fn trace_attributes(self: Pin<&mut Actor>, request: &[u8]) -> Result<Response>;
fn transit_available(self: Pin<&mut Actor>, request: &[u8]) -> Result<Response>;
fn expansion(self: Pin<&mut Actor>, request: &[u8]) -> Result<Response>;
fn centroid(self: Pin<&mut Actor>, request: &[u8]) -> Result<Response>;
fn status(self: Pin<&mut Actor>, request: &[u8]) -> Result<Response>;
fn parse_json_request(json: &str, action: i32) -> Result<UniquePtr<CxxString>>;
}
}
unsafe impl Send for ffi::Actor {}
unsafe impl Sync for ffi::Actor {}
#[derive(Debug, Clone)]
pub enum Response {
Pbf(Box<proto::Api>),
Json(String),
Other(Vec<u8>),
}
impl From<ffi::Response> for Response {
fn from(response: ffi::Response) -> Self {
if response.format == Format::Pbf as i32 {
let api = proto::Api::decode(response.data.as_bytes())
.expect("Proper PBF data is guaranteed by Valhalla");
Response::Pbf(Box::new(api))
} else if response.format == Format::Json as i32 || response.format == Format::Osrm as i32 {
Response::Json(String::from_utf8_lossy(response.data.as_bytes()).into_owned())
} else {
Response::Other(response.data.as_bytes().to_owned())
}
}
}
pub struct Actor(cxx::UniquePtr<ffi::Actor>);
impl Actor {
pub fn new(config: &Config) -> Result<Self, Error> {
Ok(Self(ffi::new_actor(config.inner())?))
}
pub fn route(&mut self, request: &proto::Options) -> Result<Response, Error> {
self.act(ffi::Actor::route, request)
}
pub fn locate(&mut self, request: &proto::Options) -> Result<Response, Error> {
self.act(ffi::Actor::locate, request)
}
pub fn matrix(&mut self, request: &proto::Options) -> Result<Response, Error> {
self.act(ffi::Actor::matrix, request)
}
pub fn optimized_route(&mut self, request: &proto::Options) -> Result<Response, Error> {
self.act(ffi::Actor::optimized_route, request)
}
pub fn isochrone(&mut self, request: &proto::Options) -> Result<Response, Error> {
self.act(ffi::Actor::isochrone, request)
}
pub fn trace_route(&mut self, request: &proto::Options) -> Result<Response, Error> {
self.act(ffi::Actor::trace_route, request)
}
pub fn trace_attributes(&mut self, request: &proto::Options) -> Result<Response, Error> {
self.act(ffi::Actor::trace_attributes, request)
}
pub fn transit_available(&mut self, request: &proto::Options) -> Result<Response, Error> {
self.act(ffi::Actor::transit_available, request)
}
pub fn expansion(&mut self, request: &proto::Options) -> Result<Response, Error> {
self.act(ffi::Actor::expansion, request)
}
pub fn centroid(&mut self, request: &proto::Options) -> Result<Response, Error> {
self.act(ffi::Actor::centroid, request)
}
pub fn status(&mut self, request: &proto::Options) -> Result<Response, Error> {
self.act(ffi::Actor::status, request)
}
fn act<F>(&mut self, action_fn: F, request: &proto::Options) -> Result<Response, Error>
where
F: for<'a> Fn(
std::pin::Pin<&'a mut ffi::Actor>,
&'a [u8],
) -> Result<ffi::Response, cxx::Exception>,
{
let buffer = request.encode_to_vec();
let result = action_fn(self.0.as_mut().unwrap(), &buffer);
Ok(Response::from(result?))
}
pub fn parse_json_request(
json: &str,
action: proto::options::Action,
) -> Result<proto::Options, Error> {
if json.is_empty() {
return Err(Error("Failed to parse json request".into()));
}
let cxx_string = ffi::parse_json_request(json, action as i32)?;
let mut options = proto::Options::decode(cxx_string.as_bytes())
.map_err(|err| Error(err.to_string().into()))?;
for costing in options.costings.values_mut() {
if let Some(proto::costing::HasOptions::Options(costing_options)) =
&mut costing.has_options
{
if let Some(proto::costing::options::HasIgnoreClosures::IgnoreClosures(false)) =
costing_options.has_ignore_closures
{
costing_options.has_ignore_closures = None;
}
}
}
Ok(options)
}
}