use std::{
sync::{Arc, LazyLock},
time::{Duration, SystemTime},
};
use async_trait::async_trait;
use nalgebra::{Isometry2, Isometry3};
use crate::{
error::Error,
gamepad::GamepadEvent,
traits::{
BaseVelocity, Gamepad, JointTrajectoryClient, Localization, MoveBase, Navigation, Speaker,
TrajectoryPoint, TransformResolver,
},
waits::WaitFuture,
};
#[allow(clippy::type_complexity)]
#[derive(Debug)]
pub struct Lazy<'a, T> {
inner: LazyLock<
Result<T, Arc<Error>>,
Box<dyn FnOnce() -> Result<T, Arc<Error>> + Send + Sync + 'a>,
>,
joint_names: Option<Vec<String>>,
}
impl<'a, T> Lazy<'a, T> {
pub fn new(constructor: impl FnOnce() -> Result<T, Error> + Send + Sync + 'a) -> Self {
Self {
inner: LazyLock::new(Box::new(|| constructor().map_err(Arc::new))),
joint_names: None,
}
}
pub fn with_joint_names(
constructor: impl FnOnce() -> Result<T, Error> + Send + Sync + 'a,
joint_names: Vec<String>,
) -> Self {
Self {
inner: LazyLock::new(Box::new(|| constructor().map_err(Arc::new))),
joint_names: Some(joint_names),
}
}
pub fn get_ref(&self) -> Result<&T, Error> {
self.inner.as_ref().map_err(|e| Error::Lazy(e.clone()))
}
}
impl<T> JointTrajectoryClient for Lazy<'_, T>
where
T: JointTrajectoryClient,
{
fn joint_names(&self) -> Vec<String> {
if let Some(joint_names) = &self.joint_names {
return joint_names.clone();
}
match self.get_ref() {
Ok(this) => this.joint_names(),
Err(e) => panic!("{e}"),
}
}
fn current_joint_positions(&self) -> Result<Vec<f64>, Error> {
self.get_ref()?.current_joint_positions()
}
fn send_joint_positions(
&self,
positions: Vec<f64>,
duration: Duration,
) -> Result<WaitFuture, Error> {
self.get_ref()?.send_joint_positions(positions, duration)
}
fn send_joint_trajectory(&self, trajectory: Vec<TrajectoryPoint>) -> Result<WaitFuture, Error> {
self.get_ref()?.send_joint_trajectory(trajectory)
}
}
impl<T> Localization for Lazy<'_, T>
where
T: Localization,
{
fn current_pose(&self, frame_id: &str) -> Result<Isometry2<f64>, Error> {
self.get_ref()?.current_pose(frame_id)
}
}
impl<T> MoveBase for Lazy<'_, T>
where
T: MoveBase,
{
fn current_velocity(&self) -> Result<BaseVelocity, Error> {
self.get_ref()?.current_velocity()
}
fn send_velocity(&self, velocity: &BaseVelocity) -> Result<(), Error> {
self.get_ref()?.send_velocity(velocity)
}
}
impl<T> Navigation for Lazy<'_, T>
where
T: Navigation,
{
fn send_goal_pose(
&self,
goal: Isometry2<f64>,
frame_id: &str,
timeout: Duration,
) -> Result<WaitFuture, Error> {
self.get_ref()?.send_goal_pose(goal, frame_id, timeout)
}
fn cancel(&self) -> Result<(), Error> {
self.get_ref()?.cancel()
}
}
impl<T> Speaker for Lazy<'_, T>
where
T: Speaker,
{
fn speak(&self, message: &str) -> Result<WaitFuture, Error> {
self.get_ref()?.speak(message)
}
}
impl<T> TransformResolver for Lazy<'_, T>
where
T: TransformResolver,
{
fn resolve_transformation(
&self,
from: &str,
to: &str,
time: SystemTime,
) -> Result<Isometry3<f64>, Error> {
self.get_ref()?.resolve_transformation(from, to, time)
}
}
#[async_trait]
impl<T> Gamepad for Lazy<'_, T>
where
T: Gamepad,
{
async fn next_event(&self) -> GamepadEvent {
match self.get_ref() {
Ok(this) => this.next_event().await,
Err(e) => panic!("{e}"),
}
}
fn stop(&self) {
match self.get_ref() {
Ok(this) => this.stop(),
Err(e) => panic!("{e}"),
}
}
}