covcon 0.3.0

Crate to convert between code coverage formats.
Documentation
use std::{ffi::OsStr, path::PathBuf};

use crate::format::CoverageFormat;

#[derive(Debug, Clone, PartialEq, Eq, clap::Parser)]
pub struct CliConfig {
    #[arg(long)]
    pub in_fmt: CoverageFormat,
    pub in_filepath: PathBuf,
    #[arg(long)]
    pub out_fmt: CoverageFormat,
    pub out_filepath: PathBuf,
}

impl TryFrom<CliConfig> for ConversionConfig {
    type Error = Box<dyn core::error::Error>;

    fn try_from(value: CliConfig) -> Result<Self, Self::Error> {
        let in_content = std::fs::read_to_string(&value.in_filepath)?;

        let in_data_fmt = DataFormat::try_from(value.in_filepath.extension())?;
        let out_data_fmt = DataFormat::try_from(value.out_filepath.extension())?;

        Ok(Self {
            in_fmt: value.in_fmt,
            in_content,
            in_data_fmt,
            out_fmt: value.out_fmt,
            out_data_fmt,
        })
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ConversionConfig {
    pub in_fmt: CoverageFormat,
    pub in_content: String,
    pub in_data_fmt: DataFormat,
    pub out_fmt: CoverageFormat,
    pub out_data_fmt: DataFormat,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DataFormat {
    Json,
    Xml,
}

impl TryFrom<Option<&OsStr>> for DataFormat {
    type Error = Box<dyn core::error::Error>;

    fn try_from(value: Option<&OsStr>) -> Result<Self, Self::Error> {
        let value = match value {
            Some(s) => s.to_string_lossy(),
            None => return Err(Box::new(Error::UnsupportedExtension)),
        };

        Self::try_from(value.as_ref())
    }
}

impl TryFrom<&str> for DataFormat {
    type Error = Box<dyn core::error::Error>;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        match value.to_lowercase().as_str() {
            "json" => Ok(Self::Json),
            "xml" => Ok(Self::Xml),
            _ => Err(Box::new(Error::UnsupportedExtension)),
        }
    }
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Error {
    UnsupportedExtension,
}

impl core::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Unsupported file extension. Only JSON and XML are currently supported."
        )
    }
}

impl core::error::Error for Error {}