use {
crate::{ParseHandednessError, Specs, Variant},
std::{
error, f64, fmt, io,
num::{ParseFloatError, ParseIntError},
path,
},
svg::{node::element::tag, parser::Event},
};
#[derive(Debug)]
pub enum Error {
Io(io::Error),
ParseFloat(ParseFloatError),
ParseInt(ParseIntError),
ParseHandedness,
NoMetadata,
MissingField(&'static str),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(e) => write!(f, "{e}"),
Self::ParseFloat(e) => write!(f, "{e}"),
Self::ParseInt(e) => write!(f, "{e}"),
Self::ParseHandedness => write!(f, "Parse handedness error"),
Self::NoMetadata => write!(f, "No metadata"),
Self::MissingField(s) => write!(f, "Missing field: {s}"),
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Self::Io(e) => Some(e),
Self::ParseFloat(e) => Some(e),
Self::ParseInt(e) => Some(e),
Self::ParseHandedness => Some(&ParseHandednessError),
Self::NoMetadata | Self::MissingField(_) => None,
}
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Self::Io(e)
}
}
impl From<ParseFloatError> for Error {
fn from(e: ParseFloatError) -> Self {
Self::ParseFloat(e)
}
}
impl From<ParseIntError> for Error {
fn from(e: ParseIntError) -> Self {
Self::ParseInt(e)
}
}
impl From<ParseHandednessError> for Error {
fn from(_: ParseHandednessError) -> Self {
Self::ParseHandedness
}
}
pub fn open<T: AsRef<path::Path>>(path: T) -> Result<Specs, Error> {
let mut content = String::new();
let event_iter = svg::open(path, &mut content)?;
for event in event_iter {
if let Event::Tag(tag::Description, _, attributes) = event {
let scale = attributes
.get("Scale")
.ok_or_else(|| Error::MissingField("Scale"))?
.parse()?;
let bridge = attributes
.get("BridgeSpacing")
.ok_or_else(|| Error::MissingField("BridgeSpacing"))?
.parse()?;
let nut = attributes
.get("NutWidth")
.ok_or_else(|| Error::MissingField("NutWidth"))?
.parse()?;
let count = attributes
.get("FretCount")
.ok_or_else(|| Error::MissingField("FretCount"))?
.parse()?;
let variant = match attributes.get("ScaleTreble") {
Some(scl) => Variant::Multiscale {
scale: scl.parse::<f64>()?,
handedness: attributes
.get("Handedness")
.ok_or_else(|| Error::MissingField("Handedness"))?
.parse()?,
pfret: attributes
.get("PerpendicularFret")
.ok_or_else(|| Error::MissingField("PerpendicularFret"))?
.parse()?,
},
None => Variant::Monoscale,
};
return Ok(Specs::init(scale, count, variant, nut, bridge));
}
}
Err(Error::NoMetadata)
}
#[cfg(test)]
mod tests {
use {super::*, crate::Handedness};
#[test]
fn test_open() {
let specs = open("src/test.svg").unwrap();
assert_eq!(specs.scale, 648.0);
assert_eq!(specs.variant.scale(), Some(610.0));
assert_eq!(specs.variant.pfret(), Some(8.5));
assert_eq!(specs.variant.handedness(), Some(Handedness::Right));
assert_eq!(specs.bridge, 56.0);
assert_eq!(specs.nut, 43.0);
assert_eq!(specs.count, 24);
}
}