mist-core 2.0.1

core functionality of mist
use crate::{
    error::Result,
    timer::{Run, TimeType},
};
use quick_xml::{events::Event, Reader};
use std::io::Read;

fn str_to_ms(tm: &str) -> u128 {
    if tm == "0:00:00" || tm == "00:00:00" || tm.is_empty() {
        0
    } else {
        let hr = tm[0..2].parse::<u128>().unwrap();
        let min = tm[3..5].parse::<u128>().unwrap();
        let sec = tm[6..8].parse::<u128>().unwrap();
        let ms = if tm.len() > 9 {
            tm[9..12].parse::<u128>().unwrap()
        } else {
            0
        };
        ms + (sec * 1000) + (min * 60000) + (hr * 3600000)
    }
}

impl Run {
    pub fn from_str_lss(lss: &str) -> Self {
        Self::parse_impl_lss(lss.as_bytes())
    }

    pub fn from_reader_lss(mut lss: impl Read) -> Result<Self> {
        let mut bytes = vec![];
        lss.read_to_end(&mut bytes)?;
        Ok(Self::parse_impl_lss(&bytes[..]))
    }

    fn parse_impl_lss(bytes: &[u8]) -> Self {
        let mut run = Run::empty();
        let mut reader = Reader::from_reader(bytes);
        reader.check_end_names(false);

        let mut time_str = String::new();
        let mut splits = vec![];
        let mut pb_times: Vec<TimeType> = vec![];
        let mut gold_times = vec![];
        let mut sum_times = vec![];
        let mut segment_sum: (u128, u128) = (0, 0);
        let mut pb = 0;

        loop {
            match reader.read_event() {
                Ok(Event::Start(ref e)) => {
                    match reader.decoder().decode(e.name().as_ref()).unwrap().as_ref() {
                        "GameName" => {
                            run.set_game_title(
                                reader
                                    .read_text(e.name())
                                    .unwrap_or("".into())
                                    .trim()
                                    .to_owned(),
                            );
                        }
                        "CategoryName" => {
                            run.set_category(
                                reader
                                    .read_text(e.name())
                                    .unwrap_or("".into())
                                    .trim()
                                    .to_owned(),
                            );
                        }
                        "Offset" => {
                            let mut off_str = reader
                                .read_text(e.name())
                                .unwrap_or("".into())
                                .trim()
                                .to_owned();
                            off_str.remove(0);
                            let t = str_to_ms(&off_str);
                            run.set_offset(t.into());
                        }
                        "Name" => {
                            splits.push(
                                reader
                                    .read_text(e.name())
                                    .unwrap_or("".into())
                                    .trim()
                                    .to_owned(),
                            );
                        }
                        "RealTime" => {
                            time_str = reader
                                .read_text(e.name())
                                .unwrap_or("".into())
                                .trim()
                                .to_owned();
                        }
                        "SegmentHistory" => {
                            segment_sum = (0, 0);
                        }
                        _ => {}
                    }
                }
                Ok(Event::End(ref e)) => {
                    match reader.decoder().decode(e.name().as_ref()).unwrap().as_ref() {
                        "SplitTime" => {
                            let t = str_to_ms(&time_str);
                            if t != 0 {
                                while pb_times.len() < gold_times.len() {
                                    pb_times.push(TimeType::None);
                                }
                                pb_times.push((t - pb).into());
                                pb = t;
                            }
                        }
                        "BestSegmentTime" => {
                            let t = str_to_ms(&time_str);
                            if t != 0 {
                                gold_times.push(t.into())
                            }
                        }
                        "Time" => {
                            let t = str_to_ms(&time_str);
                            if t != 0 {
                                segment_sum.0 += 1;
                                segment_sum.1 += t;
                            }
                        }
                        "SegmentHistory" => {
                            sum_times.push((segment_sum.0, segment_sum.1.into()));
                        }
                        _ => {}
                    }
                }
                Ok(Event::Eof) => break,
                _ => {}
            }
        }
        run.set_gold_times(&gold_times);
        run.set_pb_times(&pb_times);
        run.set_sum_times(&sum_times);
        run.set_splits(&splits);
        run.set_pb(pb.into());
        super::sanify_run(&run)
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_str_ms() {
        assert_eq!(super::str_to_ms("00:42:01.6140000"), 2521614);
        assert_eq!(super::str_to_ms("13:52:19.653"), 49939653);
        assert_eq!(super::str_to_ms("00:02:18"), 138000);
        assert_eq!(super::str_to_ms("00:00:01.2000000"), 1200);
        assert_eq!(super::str_to_ms("50:00:00"), 180000000);
    }
}