libfancontrold 0.4.0

Base library for fancontrold.
Documentation
use crate::address::{Address, Error as AddressError};

use std::{
    error::Error as StdError,
    fmt::{Display, Formatter, Result as FmtResult},
    io::Error as IoError,
    path::PathBuf,
    result::Result as StdResult,
};

use serde_json::error::Error as SerdeError;

pub type Result<T> = StdResult<T, Error>;

#[derive(Debug)]
pub enum Error {
    EmptyTemps { address: Address },
    EmptyCurve { address: Address },
    Address { source: AddressError },
    Index { address: Address },
    Average { address: Address },
    Load { path: PathBuf, source: IoError },
    Parse { source: SerdeError },
    Multiple { errors: Vec<Error> },
}

impl Error {
    pub(crate) fn new_empty_temps(address: Address) -> Self {
        Error::EmptyTemps { address }
    }

    pub(crate) fn new_empty_curve(address: Address) -> Self {
        Error::EmptyCurve { address }
    }

    pub(crate) fn new_index(address: Address) -> Self {
        Error::Index { address }
    }

    pub(crate) fn new_average(address: Address) -> Self {
        Error::Average { address }
    }

    pub(crate) fn new_load(path: impl Into<PathBuf>, source: IoError) -> Self {
        Error::Load {
            path: path.into(),
            source,
        }
    }

    pub(crate) fn from_vec(errors: Vec<Error>) -> Option<Self> {
        match errors.len() {
            0 => None,
            1 => Some(errors.into_iter().next().unwrap()),
            _ => Some(Error::Multiple { errors }),
        }
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        match self {
            Error::EmptyTemps { address } => {
                write!(f, "Config of channel at address {} has no temps.", address)
            }
            Error::EmptyCurve { address } => write!(
                f,
                "Config of channel at address {} has an empty curve.",
                address
            ),
            Error::Address { source } => write!(f, "Config has invalid sensor address: {}", source),
            Error::Index { address } => write!(
                f,
                "Config of channel at address {} has no index or index is not above zero.",
                address
            ),
            Error::Average { address } => write!(
                f,
                "Config of channel at address {} has an average below one or above 10.",
                address
            ),
            Error::Load { path, source } => {
                write!(f, "Error loading config at {}: {}", path.display(), source)
            }
            Error::Parse { source } => write!(f, "Error parsing config: {}", source),
            Error::Multiple { errors } => {
                write!(f, "Multiple errors occurred:")?;
                for error in errors {
                    write!(f, "\n{}", error)?;
                }
                Ok(())
            }
        }
    }
}

impl StdError for Error {
    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        match self {
            Error::EmptyTemps { .. } => None,
            Error::EmptyCurve { .. } => None,
            Error::Address { source } => Some(source),
            Error::Index { .. } => None,
            Error::Average { .. } => None,
            Error::Load { source, .. } => Some(source),
            Error::Parse { source } => Some(source),
            Error::Multiple { errors } => errors.first().and_then(StdError::source),
        }
    }
}

impl From<SerdeError> for Error {
    fn from(source: SerdeError) -> Self {
        Error::Parse { source }
    }
}

impl From<AddressError> for Error {
    fn from(source: AddressError) -> Self {
        Error::Address { source }
    }
}