runcc 2.0.3

run commands concurrently with rust and cargo
Documentation
use std::{error, fmt::Display, io};

use super::ConfigFormat;

#[derive(Debug)]
pub enum CargoMetadataError {
    InvalidToml(toml::de::Error),
    NoData,
    CargoTomlIsNotTable,
    InvalidDataStructure(toml::de::Error),
    FoundMultiple,
}

impl Display for CargoMetadataError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            CargoMetadataError::InvalidToml(err) => write!(f, "invalid toml: {}", err),
            CargoMetadataError::NoData => write!(f, "no field"),
            CargoMetadataError::CargoTomlIsNotTable => write!(f, "Cargo.toml is not table"),
            CargoMetadataError::InvalidDataStructure(err) => write!(f, "invalid data: {}", err),
            CargoMetadataError::FoundMultiple => write!(f, "multiple fields are found"),
        }
    }
}

impl error::Error for CargoMetadataError {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match self {
            CargoMetadataError::InvalidToml(err)
            | CargoMetadataError::InvalidDataStructure(err) => Some(err),
            _ => None,
        }
    }
}

macro_rules! enum_auto_from {
    ($(#[$a:meta])* $v:vis enum $name:ident { $($e:ident($err:path)),* $(,)? }) => {
        $(#[$a])*
        $v enum $name {
            $( $e($err) ),*
        }

        $(
            impl From<$err> for $name {
                fn from(v: $err) -> Self {
                    Self::$e(v)
                }
            }
        )*
    };
}

enum_auto_from! {
    #[derive(Debug)]
    pub enum ConfigDeserializeErrorKind {
        JsonError(serde_json::Error),
        YamlError(serde_yaml::Error),
        RonError(ron::Error),
        TomlError(toml::de::Error),
        CargoMetadataError(CargoMetadataError),
    }
}

impl Display for ConfigDeserializeErrorKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ConfigDeserializeErrorKind::JsonError(err) => write!(f, "{}", err),
            ConfigDeserializeErrorKind::YamlError(err) => write!(f, "{}", err),
            ConfigDeserializeErrorKind::RonError(err) => write!(f, "{}", err),
            ConfigDeserializeErrorKind::TomlError(err) => write!(f, "{}", err),
            ConfigDeserializeErrorKind::CargoMetadataError(err) => write!(f, "{}", err),
        }
    }
}

impl error::Error for ConfigDeserializeErrorKind {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match self {
            ConfigDeserializeErrorKind::JsonError(err) => Some(err),
            ConfigDeserializeErrorKind::YamlError(err) => Some(err),
            ConfigDeserializeErrorKind::RonError(err) => Some(err),
            ConfigDeserializeErrorKind::TomlError(err) => Some(err),
            ConfigDeserializeErrorKind::CargoMetadataError(err) => Some(err),
        }
    }
}

#[non_exhaustive]
#[derive(Debug)]
pub struct ConfigDeserializeError {
    pub filename: String,
    pub format: ConfigFormat,
    pub kind: ConfigDeserializeErrorKind,
}

impl error::Error for ConfigDeserializeError {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        self.kind.source()
    }
}

impl Display for ConfigDeserializeError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Invalid config file {}: {}", self.filename, self.kind)
    }
}

#[derive(Debug)]
pub enum ReadConfigError {
    DeserializeError(ConfigDeserializeError),
    OpenFileError { file: String, error: io::Error },
}

impl error::Error for ReadConfigError {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match self {
            ReadConfigError::DeserializeError(err) => Some(err),
            ReadConfigError::OpenFileError { error, .. } => Some(error),
        }
    }
}

impl Display for ReadConfigError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ReadConfigError::DeserializeError(err) => write!(f, "{}", err),
            ReadConfigError::OpenFileError { error, file } => {
                write!(f, "Failed to open file \"{}\": {}", file, error)
            }
        }
    }
}

#[derive(Debug)]
pub enum FindConfigError {
    NoFileMatch {
        patterns: Vec<String>,
        dir: String,
    },
    ReadError(ReadConfigError),
    UnknownExtension {
        file: String,
        extension: Option<String>,
    },
}

impl error::Error for FindConfigError {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        None
    }
}

impl Display for FindConfigError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            FindConfigError::NoFileMatch { patterns, dir } => write!(
                f,
                r#"No files in "{}" matched the patterns: {}"#,
                dir,
                patterns.join(", ")
            ),
            FindConfigError::ReadError(err) => write!(f, "{}", err),
            FindConfigError::UnknownExtension { file, extension } => {
                write!(
                    f,
                    "Unknown extension \"{}\" for file \"{}\"",
                    if let Some(extension) = extension {
                        extension
                    } else {
                        "none"
                    },
                    file
                )
            }
        }
    }
}

impl From<ConfigDeserializeError> for ReadConfigError {
    fn from(err: ConfigDeserializeError) -> Self {
        Self::DeserializeError(err)
    }
}

impl From<ReadConfigError> for FindConfigError {
    fn from(err: ReadConfigError) -> Self {
        Self::ReadError(err)
    }
}