diff options
Diffstat (limited to 'src/seq/file/iv.rs')
-rw-r--r-- | src/seq/file/iv.rs | 224 |
1 files changed, 143 insertions, 81 deletions
diff --git a/src/seq/file/iv.rs b/src/seq/file/iv.rs index de3f0b6..24b69e7 100644 --- a/src/seq/file/iv.rs +++ b/src/seq/file/iv.rs @@ -1,111 +1,173 @@ use std::io; -use std::collections::HashMap; +use std::convert::TryFrom; +use std::str::{from_utf8, Utf8Error}; use std::borrow::Borrow; -use xml::reader; -use xml::reader::{EventReader, XmlEvent}; -use xml::attribute::OwnedAttribute; -use std::hash::Hash; -use std::cmp::Eq; -use std::str::FromStr; -use std::fmt::Display; -use failure::Error; +use crate::seq::{IV, Version, VersionDecodeError, IVMeta, BPMTable}; -struct AttrMapping(HashMap<String, String>); +use quick_xml::events::{Event, BytesStart}; -impl AttrMapping { - pub fn make(attrs: Vec<OwnedAttribute>) -> AttrMapping { - let mut output = HashMap::new(); - for attr in attrs { - output.insert(attr.name.local_name.clone(), attr.value.clone()); - } +struct State<'s, B: io::BufRead> { + iv: &'s mut IV, + rdr: &'s mut quick_xml::Reader<B>, +} - AttrMapping(output) - } +#[derive(Debug)] +pub enum Error { + QXML(quick_xml::Error), + VersionDecodeError, + UTF8(Utf8Error), + Unexpected { scope: Scope, event: String }, +} - pub fn get_str<'a, 'b, 'c: 'a, Q: Hash+Eq+Display+?Sized>(&'a self, key: &'b Q, default: &'c str) -> &'a str where String: Borrow<Q> { - self.0.get(key).map(|x| &**x).unwrap_or(default) - } +#[derive(Debug, Clone, Copy, Hash)] +pub enum Scope { + TopLevel, +} - pub fn req<V: FromStr, Q: Hash+Eq+Display+?Sized>(&self, key: &Q) -> Result<V, Error> where String: Borrow<Q>, V::Err: failure::Fail { - match self.0.get(key){ - Some(x) => Ok(x.parse()?), - None => bail!("{} not found in attrs", key) - } +impl From<quick_xml::Error> for Error { + fn from(t: quick_xml::Error) -> Self { + Error::QXML(t) } +} - pub fn req_midi_pitch<Q: Hash+Eq+Display+?Sized>(&self, key: &Q) -> Result<Pitch, Error> where String: Borrow<Q> { - Ok(Pitch::MIDI(self.req::<f32, Q>(key)?)) +impl From<VersionDecodeError> for Error { + fn from(v: VersionDecodeError) -> Self { + Error::VersionDecodeError } } -fn parse_note(ev: XmlEvent, into: &mut Vec<Note>) -> Result<bool, Error> { - match ev { - XmlEvent::StartElement{name, attributes, ..} => { - if name.local_name.as_ref() != "note" { bail!("malformed iv: non-note attr in note stream"); } - let attrs = AttrMapping::make(attributes); - into.push(Note { - time: attrs.req("time")?, - ampl: attrs.req("ampl")?, - dur: attrs.req("dur")?, - pitch: attrs.req_midi_pitch("pitch")?, - start_tick: None, - dur_ticks: None - }); - Ok(false) - }, - _ => Ok(true) +impl From<Utf8Error> for Error { + fn from(e: Utf8Error) -> Self { + Error::UTF8(e) } } pub fn read<R: io::Read>(source: R) -> Result<IV, Error> { let mut output: IV = Default::default(); - let mut event_reader = EventReader::new(source); - - #[derive(Debug)] - enum ReadState<'a> { - Idle, - InStreams, - InBPMs, - InNoteStream(&'a mut NoteStream), - InAuxStream(&'a mut AuxStream), + let mut reader = quick_xml::Reader::from_reader( + io::BufReader::new(source) + ); + let mut state = State { + iv: &mut output, + rdr: &mut reader, + }; + + read_toplevel(&mut state)?; + + Ok(output) +} + +const IV_NAME: &[u8] = b"iv"; +const IV_VERSION: &[u8] = b"version"; +const IV_SOURCE: &[u8] = b"src"; +fn read_toplevel<'s, B: io::BufRead>(state: &mut State<'s, B>) -> Result<(), Error> { + let mut buffer: Vec<u8> = Vec::new(); + loop { + match state.rdr.read_event(&mut buffer)? { + Event::Decl(_) => (), // Don't care + Event::Start(bs) => { + match_iv(state, bs, false); + break; + }, + Event::Empty(bs) => { + match_iv(state, bs, true); + break; + }, + ev => return Err(Error::Unexpected { + scope: Scope::TopLevel, + event: format!("{:?}", ev), + }), + } + } + Ok(()) +} + +fn match_iv<'s, 'a, B: io::BufRead>(state: &mut State<'s, B>, bs: BytesStart<'a>, empty: bool) -> Result<(), Error> { + if bs.name() != IV_NAME { + return Err(Error::Unexpected { + scope: Scope::TopLevel, + event: format!("start tag: {:?}", bs.name()), + }); + } + for attr in bs.attributes() { + let attr = attr?; + match attr.key { + key if key == IV_VERSION => { + let value = attr.unescaped_value()?; + state.iv.version = + Version::try_from(value.borrow())?; + }, + key if key == IV_SOURCE => { + state.iv.source = + Some(from_utf8( + attr.unescaped_value()?.borrow() + )?.into()); + }, + _ => (), + } } + if !empty { read_iv(state)?; } + Ok(()) +} - let mut state = ReadState::Idle; +const META_NAME: &[u8] = b"meta"; +const STREAMS_NAME: &[u8] = b"streams"; +fn read_iv<'s, B: io::BufRead>(state: &mut State<'s, B>) -> Result<(), Error> { + let mut buffer: Vec<u8> = Vec::new(); + loop { + match state.rdr.read_event(&mut buffer)? { + Event::Start(bs) => { + match_in_iv(state, bs, false); + }, + Event::Empty(bs) => { + match_in_iv(state, bs, true); + }, + Event::End(be) => { + if be.name() == IV_NAME { + break; + } + }, + _ => (), + } + } + Ok(()) +} +fn read_until<'s, B: io::BufRead>(state: &mut State<'s, B>, name: &[u8]) -> Result<(), Error> { + let mut buffer: Vec<u8> = Vec::new(); loop { - match event_reader.next()? { - XmlEvent::StartElement{name, attributes, ..} => { - let attrmap = AttrMapping::make(attributes); - - match name.local_name.as_ref() { - "bpms" => { } - "streams" => { - match attrmap.get_str("type", "") { - "ns" => { - let mut notes = Vec::new(); - - loop { - if !parse_note(event_reader.next()?, &mut notes)? { break; } - } - - }, - _ => unimplemented!() - } - }, - _ => unimplemented!() + match state.rdr.read_event(&mut buffer)? { + Event::End(be) => { + if be.name() == name { + return Ok(()); } } - XmlEvent::EndElement{name} => match (name.local_name.as_ref(), &state) { - ("bpms", _) => { state = ReadState::Idle; }, - ("streams", _) => { state = ReadState::Idle; }, - _ => (), - }, _ => (), } } - +} - Ok(output) +fn match_in_iv<'s, 'a, B: io::BufRead>(state: &mut State<'s, B>, bs: BytesStart<'a>, empty: bool) -> Result<(), Error> { + match bs.name() { + nm if nm == META_NAME => { + if !empty { read_meta(state)?; } + }, + nm if nm == STREAMS_NAME => { + if !empty { read_streams(state)?; } + }, + nm => { + if !empty { read_until(state, nm.borrow())?; } + } + } + Ok(()) +} + +fn read_meta<'s, B: io::BufRead>(state: &mut State<'s, B>) -> Result<(), Error> { + todo!() +} + +fn read_streams<'s, B: io::BufRead>(state: &mut State<'s, B>) -> Result<(), Error> { + todo!() } |