libmedium 0.13.4

Library to interface with lm_sensors
Documentation
//! Module containing the virtual sensors and their related functionality.

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]
/// Helper trait that sums up all functionality of a read-only virtual sensor.
pub trait AsyncVirtualSensor<T: Raw> {
    /// Reads the virtual sensor.
    async fn read(&self) -> Result<T>;
}

/// Struct that represents a virtual sensor.
#[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)),
            },
        }
    }
}

/// Struct that represents a virtual sensor with a custom read function.
#[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)
    }
}

/// Struct that represents a virtual sensor with custom read and write function.
#[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]
/// Helper trait that sums up all functionality of a read-write virtual sensor.
pub trait AsyncWriteableVirtualSensor<T: Raw>: AsyncVirtualSensor<T> {
    /// Writes to the virtual sensor.
    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)
    }
}

/// Creates a virtual sensor from the given file at `path`.
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 })
}

/// Creates a virtual sensor from a custom read function.
/// Virtual sensors with custom read and write functions are not `Send`.
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 }
}

/// Creates a virtual sensor from a custom read function that is `Send`.
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 }
}

/// Creates a virtual sensor from a custom read function that is `Clone`.
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")]
/// Creates a writable virtual sensor from the given file at `path`.
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")]
/// Creates a virtual sensor from custom read and write functions.
/// Virtual sensors with custom read and write functions are not `Send`.
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")]
/// Creates a virtual sensor from custom read and write functions that is `Send`.
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")]
/// Creates a virtual sensor from custom read and write functions that is `Clone`.
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 }
}