edar 1.0.0

A tool to extract metadata from an audio file
Documentation
use std::{fmt, time::Duration};

pub mod extraction;

pub use extraction::extract_metadata;

#[derive(Debug, Clone, Default)]
pub struct Metadata {
    pub title: Option<String>,
    pub artist: Option<String>,
    pub album: Option<String>,
    pub year: Option<String>,
    pub genre: Option<String>,
    pub cover: Option<Vec<u8>>,

    pub duration: Option<Duration>,
    pub sample_rate: Option<u32>,
    pub channels: Option<u8>,
    pub has_tag: bool,
}

#[derive(Debug)]
pub enum MetadataError {
    ReadError,
    NoTag,

    NoTitle,
    NoArtist,
    NoAlbum,
    NoYear,
    NoGenre,
    NoCover,
    NoDuration,
    NoSampleRate,
    NoChannels,
    NoBitrate,
}

impl fmt::Display for Metadata {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.title.as_deref().unwrap_or(""))?;
        write!(f, "{}", self.artist.as_deref().unwrap_or(""))?;
        write!(f, "{}", self.album.as_deref().unwrap_or(""))?;
        write!(f, "{}", self.year.as_deref().unwrap_or(""))?;
        write!(f, "{}", self.genre.as_deref().unwrap_or(""))?;
        Ok(())
    }
}

impl fmt::Display for MetadataError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let text = match self {
            MetadataError::ReadError => "Couldnt read file.",
            MetadataError::NoTag => "File has no tag",
            MetadataError::NoTitle => "Tag has no title",
            MetadataError::NoArtist => "Tag has no artist",
            MetadataError::NoAlbum => "Tag has no album",
            MetadataError::NoYear => "Tag has no year",
            MetadataError::NoGenre => "Tag has no genre",
            MetadataError::NoCover => "Tag has no cover",
            MetadataError::NoDuration => "Tag has no duration",
            MetadataError::NoSampleRate => "Tag has no sample rate",
            MetadataError::NoChannels => "Tag has no channels",
            MetadataError::NoBitrate => "Tag has no bit rate",
        };
        write!(f, "{text}")
    }
}

pub trait FormatDuration {
    fn format(&self) -> String;
}

impl FormatDuration for Option<Duration> {
    fn format(&self) -> String {
        match self {
            Some(duration) => {
                let minutes = duration.as_secs() / 60;
                let seconds = duration.as_secs() % 60;
                format!("{:02}:{:02}", minutes, seconds)
            }
            None => "00:00".to_string(),
        }
    }
}