1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
pub use crate::artist::ArtistsReader;
pub use crate::label::LabelsReader;
pub use crate::master::MastersReader;
pub use crate::release::ReleasesReader;
use flate2::read::GzDecoder;
use quick_xml::events::Event;
use quick_xml::Error as XmlError;
use std::fmt;
use std::fs::File;
use std::io::{BufRead, BufReader, Error as IoError};
use std::path::Path;
use thiserror::Error;

pub type XmlReader = quick_xml::Reader<Box<dyn BufRead>>;

pub fn get_xml_reader(path: &Path) -> Result<XmlReader, IoError> {
    let file = File::open(path)?;
    let gz = GzDecoder::new(file);
    let reader: Box<dyn BufRead> = if gz.header().is_some() {
        Box::new(BufReader::new(gz))
    } else {
        let file = File::open(path)?;
        Box::new(BufReader::new(file))
    };
    Ok(quick_xml::Reader::from_reader(reader))
}

pub enum DiscogsReader {
    Artists(Box<ArtistsReader>),
    Labels(Box<LabelsReader>),
    Masters(Box<MastersReader>),
    Releases(Box<ReleasesReader>),
}

impl DiscogsReader {
    pub fn from_path(path: &Path) -> Result<DiscogsReader, ReaderError> {
        let mut xml_reader = get_xml_reader(path)?;
        let mut buf = Vec::with_capacity(4096);
        let start_event = loop {
            match xml_reader.read_event_into(&mut buf)? {
                Event::Start(ev) => break ev,
                Event::Eof => return Err(ReaderError::NoStartTag),
                _ => continue,
            }
        };
        let reader = match start_event.name().as_ref() {
            b"artists" => DiscogsReader::Artists(Box::new(ArtistsReader::new(xml_reader, buf))),
            b"labels" => DiscogsReader::Labels(Box::new(LabelsReader::new(xml_reader, buf))),
            b"masters" => DiscogsReader::Masters(Box::new(MastersReader::new(xml_reader, buf))),
            b"releases" => DiscogsReader::Releases(Box::new(ReleasesReader::new(xml_reader, buf))),
            _ => {
                return Err(ReaderError::InvalidStartTag);
            }
        };
        Ok(reader)
    }
}

#[derive(Error, Debug)]
pub enum ReaderError {
    #[error(transparent)]
    IoError(#[from] IoError),
    #[error(transparent)]
    XmlError(#[from] XmlError),
    #[error("No start tag present in file")]
    NoStartTag,
    #[error("Invalid start tag present in file")]
    InvalidStartTag,
}

impl fmt::Display for DiscogsReader {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = match &self {
            DiscogsReader::Artists(_) => "artists",
            DiscogsReader::Labels(_) => "labels",
            DiscogsReader::Masters(_) => "masters",
            DiscogsReader::Releases(_) => "releases",
        };
        write!(f, "{name}")
    }
}