1#![doc = include_str!("../README.md")]
2#![warn(missing_debug_implementations, missing_docs, rust_2018_idioms)]
3#![allow(clippy::derive_partial_eq_without_eq)]
5#![allow(repr_transparent_external_private_fields)]
7
8mod proxy;
9
10#[rustfmt::skip]
11#[path = "gen/api.rs"]
12mod api;
13
14use std::{fmt, path::Path, sync::Arc, time::Duration};
15
16use abi_stable::{erased_types::TD_Opaque, library::lib_header_from_path, StableAbi};
17use arci::{async_trait, WaitFuture};
18
19pub use crate::api::*;
20#[doc(hidden)]
22pub use crate::proxy::PluginMod_Ref;
23use crate::proxy::{GamepadTraitObject, JointTrajectoryClientTraitObject};
24
25#[macro_export]
40macro_rules! export_plugin {
41 ($plugin_constructor:expr $(,)?) => {
42 #[allow(clippy::drop_non_drop)] #[::abi_stable::export_root_module]
47 pub fn instantiate_root_module() -> $crate::PluginMod_Ref {
48 $crate::PluginMod_Ref::new(plugin_constructor)
49 }
50
51 #[allow(clippy::drop_non_drop)] #[::abi_stable::sabi_extern_fn]
54 pub fn plugin_constructor() -> $crate::PluginProxy {
55 $crate::PluginProxy::new($plugin_constructor)
56 }
57 };
58}
59
60impl PluginProxy {
61 pub fn from_path(path: impl AsRef<Path>) -> Result<Self, arci::Error> {
63 let path = path.as_ref();
64
65 let header = lib_header_from_path(path).map_err(anyhow::Error::from)?;
66 let root_module = header
67 .init_root_module::<PluginMod_Ref>()
68 .map_err(anyhow::Error::from)?;
69
70 let plugin_constructor = root_module.plugin_constructor();
71 let plugin = plugin_constructor();
72
73 Ok(plugin)
74 }
75}
76
77impl fmt::Debug for PluginProxy {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 f.debug_struct("PluginProxy").finish()
80 }
81}
82
83#[repr(C)]
88#[derive(StableAbi)]
89pub struct JointTrajectoryClientProxy(JointTrajectoryClientTraitObject);
90
91impl JointTrajectoryClientProxy {
92 pub fn new<T>(client: T) -> Self
94 where
95 T: arci::JointTrajectoryClient + 'static,
96 {
97 Self(JointTrajectoryClientTraitObject::from_value(
98 client, TD_Opaque,
99 ))
100 }
101}
102
103impl arci::JointTrajectoryClient for JointTrajectoryClientProxy {
104 fn joint_names(&self) -> Vec<String> {
105 self.0.joint_names().into_iter().map(|s| s.into()).collect()
106 }
107
108 fn current_joint_positions(&self) -> Result<Vec<f64>, arci::Error> {
109 Ok(self
110 .0
111 .current_joint_positions()
112 .into_result()?
113 .into_iter()
114 .map(f64::from)
115 .collect())
116 }
117
118 fn send_joint_positions(
119 &self,
120 positions: Vec<f64>,
121 duration: Duration,
122 ) -> Result<WaitFuture, arci::Error> {
123 Ok(self
124 .0
125 .send_joint_positions(
126 positions.into_iter().map(Into::into).collect(),
127 duration.into(),
128 )
129 .into_result()?
130 .into())
131 }
132
133 fn send_joint_trajectory(
134 &self,
135 trajectory: Vec<arci::TrajectoryPoint>,
136 ) -> Result<WaitFuture, arci::Error> {
137 Ok(self
138 .0
139 .send_joint_trajectory(trajectory.into_iter().map(Into::into).collect())
140 .into_result()?
141 .into())
142 }
143}
144
145impl fmt::Debug for JointTrajectoryClientProxy {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 f.debug_struct("JointTrajectoryClientProxy").finish()
148 }
149}
150
151#[repr(C)]
157#[derive(StableAbi)]
158pub struct GamepadProxy(GamepadTraitObject);
159
160impl GamepadProxy {
161 pub fn new<T>(gamepad: T) -> Self
163 where
164 T: arci::Gamepad + 'static,
165 {
166 Self(GamepadTraitObject::from_value(Arc::new(gamepad), TD_Opaque))
167 }
168}
169
170#[async_trait]
171impl arci::Gamepad for GamepadProxy {
172 async fn next_event(&self) -> arci::gamepad::GamepadEvent {
173 let this = Self(self.0.clone());
174 tokio::task::spawn_blocking(move || this.0.next_event().into())
175 .await
176 .unwrap_or(arci::gamepad::GamepadEvent::Unknown)
177 }
178
179 fn stop(&self) {
180 self.0.stop();
181 }
182}
183
184impl fmt::Debug for GamepadProxy {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 f.debug_struct("GamepadProxy").finish()
187 }
188}