use super::*;
use crate::units::Raw;
use derive_where::derive_where;
use std::{
error::Error as StdError, fmt::Debug, future::Future, io::ErrorKind as IOErrorKind,
path::PathBuf, result::Result as StdResult,
};
#[async_trait]
pub trait AsyncVirtualSensor<T: Raw> {
async fn read(&self) -> Result<T>;
}
#[derive(Debug, Clone)]
pub(crate) struct VirtualSensorStruct {
path: PathBuf,
}
#[async_trait]
impl<T: Raw> AsyncVirtualSensor<T> for VirtualSensorStruct {
async fn read(&self) -> Result<T> {
match tokio::fs::read_to_string(&self.path).await {
Ok(value) => T::from_raw(&value).map_err(Error::from),
Err(e) => match e.kind() {
IOErrorKind::PermissionDenied => Err(Error::insufficient_rights(&self.path)),
_ => Err(Error::read(e, &self.path)),
},
}
}
}
#[derive_where(Clone; RF)]
#[derive_where(Debug)]
#[derive_where(skip_inner(Debug))]
pub(crate) struct CustomReadVirtualSensorStruct<T, RF, RR, E>
where
RF: Fn() -> RR,
RR: Future<Output = StdResult<T, E>>,
E: StdError + 'static,
{
read_fn: RF,
}
#[async_trait]
impl<T, RF, RR, E> AsyncVirtualSensor<T> for CustomReadVirtualSensorStruct<T, RF, RR, E>
where
T: Raw,
RF: Fn() -> RR + Sync,
RR: Future<Output = StdResult<T, E>> + Send,
E: StdError + Send + 'static,
{
async fn read(&self) -> Result<T> {
(self.read_fn)().await.map_err(Error::custom_virtual)
}
}
#[cfg(feature = "writeable")]
#[derive_where(Clone; RF, WF)]
#[derive_where(Debug)]
#[derive_where(skip_inner(Debug))]
pub(crate) struct CustomReadWriteVirtualSensorStruct<T, RF, WF, RR, RW, E>
where
RF: Fn() -> RR,
WF: Fn(&T) -> RW,
RR: Future<Output = StdResult<T, E>>,
RW: Future<Output = StdResult<(), E>>,
E: StdError + Send + 'static,
{
read_fn: RF,
write_fn: WF,
}
#[cfg(feature = "writeable")]
#[async_trait]
impl<T, RF, WF, RR, RW, E> AsyncVirtualSensor<T>
for CustomReadWriteVirtualSensorStruct<T, RF, WF, RR, RW, E>
where
T: Raw,
RF: Fn() -> RR + Sync,
WF: Fn(&T) -> RW + Sync,
RR: Future<Output = StdResult<T, E>> + Send,
RW: Future<Output = StdResult<(), E>>,
E: StdError + Send + 'static,
{
async fn read(&self) -> Result<T> {
(self.read_fn)().await.map_err(Error::custom_virtual)
}
}
#[cfg(feature = "writeable")]
#[async_trait]
pub trait AsyncWriteableVirtualSensor<T: Raw>: AsyncVirtualSensor<T> {
async fn write(&self, value: &T) -> Result<()>;
}
#[cfg(feature = "writeable")]
#[async_trait]
impl<T> AsyncWriteableVirtualSensor<T> for VirtualSensorStruct
where
T: Raw + Sync,
{
async fn write(&self, value: &T) -> Result<()> {
tokio::fs::write(&self.path, value.to_raw().as_bytes())
.await
.map_err(|e| match e.kind() {
std::io::ErrorKind::PermissionDenied => Error::insufficient_rights(&self.path),
_ => Error::write(e, &self.path),
})
}
}
#[cfg(feature = "writeable")]
#[async_trait]
impl<T, RF, WF, RR, RW, E> AsyncWriteableVirtualSensor<T>
for CustomReadWriteVirtualSensorStruct<T, RF, WF, RR, RW, E>
where
T: Raw + Sync,
RF: Fn() -> RR + Sync,
WF: Fn(&T) -> RW + Sync,
RR: Future<Output = StdResult<T, E>> + Send,
RW: Future<Output = StdResult<(), E>> + Send,
E: StdError + Send + 'static,
{
async fn write(&self, value: &T) -> Result<()> {
(self.write_fn)(value).await.map_err(Error::custom_virtual)
}
}
pub fn virtual_sensor_from_path<T: Raw + Clone>(
path: impl Into<PathBuf>,
) -> Result<impl AsyncVirtualSensor<T> + Clone + Send + Sync + Debug + 'static> {
let path = path.into();
if !path.is_file() {
return Err(Error::read(
std::io::Error::from(std::io::ErrorKind::NotFound),
path,
));
}
Ok(VirtualSensorStruct { path })
}
pub fn custom_virtual_sensor<T, RF, RR, E>(read_fn: RF) -> impl AsyncVirtualSensor<T> + Sync + Debug
where
T: Raw,
RF: Fn() -> RR + Sync,
RR: Future<Output = StdResult<T, E>> + Send,
E: StdError + Send + 'static,
{
CustomReadVirtualSensorStruct { read_fn }
}
pub fn custom_virtual_sensor_send<T, RF, RR, E>(
read_fn: RF,
) -> impl AsyncVirtualSensor<T> + Send + Sync + Debug
where
T: Raw,
RF: Fn() -> RR + Send + Sync,
RR: Future<Output = StdResult<T, E>> + Send,
E: StdError + Send + 'static,
{
CustomReadVirtualSensorStruct { read_fn }
}
pub fn custom_virtual_sensor_clone<T, RF, RR, E>(
read_fn: RF,
) -> impl AsyncVirtualSensor<T> + Sync + Clone + Debug
where
T: Raw,
RF: Fn() -> RR + Clone + Sync,
RR: Future<Output = StdResult<T, E>> + Send,
E: StdError + Send + 'static,
{
CustomReadVirtualSensorStruct { read_fn }
}
#[cfg(feature = "writeable")]
pub fn writeable_virtual_sensor_from_path<T: Raw + Sync>(
path: impl Into<PathBuf>,
) -> Result<impl AsyncWriteableVirtualSensor<T> + Clone + Send + Sync + Debug + 'static> {
let path = path.into();
if !path.is_file() {
return Err(Error::read(
std::io::Error::from(std::io::ErrorKind::NotFound),
path,
));
}
Ok(VirtualSensorStruct { path })
}
#[cfg(feature = "writeable")]
pub fn custom_writeable_virtual_sensor<T, RF, WF, RR, WR, E>(
read_fn: RF,
write_fn: WF,
) -> impl AsyncVirtualSensor<T> + Sync + Debug
where
T: Raw,
RF: Fn() -> RR + Sync,
WF: Fn(&T) -> WR + Sync,
RR: Future<Output = StdResult<T, E>> + Send,
WR: Future<Output = StdResult<(), E>> + Send,
E: StdError + Send + 'static,
{
CustomReadWriteVirtualSensorStruct { read_fn, write_fn }
}
#[cfg(feature = "writeable")]
pub fn custom_writeable_virtual_sensor_send<T, RF, WF, RR, WR, E>(
read_fn: RF,
write_fn: WF,
) -> impl AsyncVirtualSensor<T> + Send + Sync + Debug
where
T: Raw,
RF: Fn() -> RR + Send + Sync,
WF: Fn(&T) -> WR + Send + Sync,
RR: Future<Output = StdResult<T, E>> + Send,
WR: Future<Output = StdResult<(), E>> + Send,
E: StdError + Send + 'static,
{
CustomReadWriteVirtualSensorStruct { read_fn, write_fn }
}
#[cfg(feature = "writeable")]
pub fn custom_writeable_virtual_sensor_clone<T, RF, WF, RR, WR, E>(
read_fn: RF,
write_fn: WF,
) -> impl AsyncVirtualSensor<T> + Sync + Clone + Debug
where
T: Raw,
RF: Fn() -> RR + Clone + Sync,
WF: Fn(&T) -> WR + Clone + Sync,
RR: Future<Output = StdResult<T, E>> + Send,
WR: Future<Output = StdResult<(), E>> + Send,
E: StdError + Send + 'static,
{
CustomReadWriteVirtualSensorStruct { read_fn, write_fn }
}