use crate::comment::Comment;
use crate::error::Error;
use crate::header::Header;
use crate::parser::Command;
use crate::parser::Parna;
use crate::track::Track;
use crate::track::Disc;
use crate::trim_utf8_header;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Cursor;
use std::io::Read;
use std::iter::Flatten;
use std::ops::Index;
use std::path::Path;
use std::slice::Iter;
use std::str::FromStr;
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
pub struct Cuna {
pub header: Header,
pub files: Vec<Disc>,
pub comments: Comment,
}
impl Cuna {
#[inline(always)]
pub fn new(s: &str) -> Result<Self, Error> {
s.parse()
}
pub fn new_suc(s: &str) -> Self {
let cursor = Cursor::new(s);
Self::from_buf_read_suc(cursor).unwrap()
}
pub const fn with_parts(header: Header, files: Vec<Disc>, comments: Comment) -> Self {
Self {
header,
files,
comments,
}
}
pub fn read(mut f: impl Read) -> Result<Self, Error> {
let mut buf = String::new();
f.read_to_string(&mut buf)?;
buf.parse()
}
pub fn read_suc(mut f: impl Read) -> Result<Self, Error> {
let mut buf = String::new();
f.read_to_string(&mut buf)?;
Ok(Self::new_suc(&buf))
}
pub fn from_file(file: &File) -> Result<Self, Error> {
let buffer = BufReader::new(file);
Self::from_buf_read(buffer)
}
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
let file = File::open(path)?;
Self::from_file(&file)
}
pub fn open_suc<P: AsRef<Path>>(path: P) -> std::io::Result<Self> {
let buffer = BufReader::new(File::open(path)?);
Self::from_buf_read_suc(buffer)
}
pub fn from_buf_read(mut buf: impl BufRead) -> Result<Self, Error> {
let mut sheet = Self::default();
let mut buffer = String::new();
let mut at = 1;
loop {
match buf.read_line(&mut buffer) {
Ok(0) => break Ok(sheet),
Ok(_) => {
let err = |e| Error::new(e, at);
let command = Command::new(trim_utf8_header(&buffer)).map_err(err)?;
command.parse(&mut sheet).map_err(err)?;
}
Err(e) => break Err(Error::new(e.into(), at)),
}
at += 1;
buffer.clear();
}
}
pub fn from_buf_read_suc(mut buf: impl BufRead) -> std::io::Result<Self> {
let mut sheet = Self::default();
let mut buffer = String::new();
loop {
match buf.read_line(&mut buffer) {
Ok(0) => break Ok(sheet),
Ok(_) => {
let command = match Command::new(trim_utf8_header(&buffer)) {
Ok(c) => c,
_ => continue,
};
let _ = command.parse(&mut sheet);
}
Err(e) => break Err(e),
}
buffer.clear();
}
}
pub fn header(&self) -> &Header {
&self.header
}
pub fn title(&self) -> &Vec<String> {
self.header.title()
}
pub fn performer(&self) -> &Vec<String> {
self.header.performer()
}
pub fn songwriter(&self) -> &Vec<String> {
self.header.songwriter()
}
pub fn catalog(&self) -> Option<u64> {
self.header.catalog()
}
pub fn files(&self) -> &Vec<Disc> {
&self.files
}
pub fn set_files(&mut self, files: Vec<Disc>) -> Vec<Disc> {
std::mem::replace(&mut self.files, files)
}
pub fn comments(&self) -> &Comment {
&self.comments
}
pub fn push_file(&mut self, track: Disc) {
self.files.push(track);
}
pub fn first_file(&self) -> Option<&Disc> {
self.files.first()
}
pub fn first_file_mut(&mut self) -> Option<&mut Disc> {
self.files.first_mut()
}
pub fn last_file(&self) -> Option<&Disc> {
self.files.last()
}
pub fn last_file_mut(&mut self) -> Option<&mut Disc> {
self.files.last_mut()
}
pub fn last_track(&self) -> Option<&Track> {
self.last_file().and_then(Disc::last_track)
}
pub fn last_track_mut(&mut self) -> Option<&mut Track> {
self.last_file_mut().and_then(Disc::last_track_mut)
}
pub fn tracks(&self) -> Flatten<Iter<Disc>> {
self.files.iter().flatten()
}
}
impl FromStr for Cuna {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut sheet = Cuna::default();
Parna::new(crate::trim_utf8_header(s)).parse(&mut sheet)?;
Ok(sheet)
}
}
impl Index<usize> for Cuna {
type Output = Disc;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
&self.files[index]
}
}