use crate::utils::consts::{
METADATA_PATH, PROJECT_CONTENT_TYPE, PROJECT_FIXED_CONTENT_TYPE, PROJECT_PATH,
};
use crate::{Dawproject, DawprojectWithZip, MetaData, Project};
use std::fs::File;
use std::io::{BufReader, Read, Seek};
use std::path::Path;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum DawprojectReadError {
#[error("zip extraction error")]
ZipError(#[from] zip::result::ZipError),
#[error("metadata.xml deserialize error: {0}")]
MetadataDeserializeError(String),
#[error("project.xml deserialize error: {0}")]
ProjectDeserializeError(String),
#[error("std io error")]
StdIoError(#[from] std::io::Error),
}
#[derive(Clone, Debug)]
pub struct DawprojectReader<R> {
zip: zip::ZipArchive<R>,
file_names: Vec<String>,
metadata: Option<MetaData>,
project: Option<Project>,
}
impl<R> DawprojectReader<R>
where
R: Read + Seek,
{
pub fn new(reader: R) -> Result<Self, DawprojectReadError> {
let zip = zip::ZipArchive::new(reader)?;
let mut file_names = zip
.file_names()
.map(|f| f.to_string())
.collect::<Vec<String>>();
file_names.sort();
Ok(DawprojectReader {
zip,
file_names,
metadata: None,
project: None,
})
}
pub fn into_inner(self) -> zip::ZipArchive<R> {
self.zip
}
fn read_metadata(&mut self) -> Result<(), DawprojectReadError> {
let mut metadata_xml = self.zip.by_name(METADATA_PATH)?;
let mut metadata_str = String::new();
metadata_xml.read_to_string(&mut metadata_str)?;
let metadata: MetaData = quick_xml::de::from_str(&metadata_str)
.map_err(|e| DawprojectReadError::MetadataDeserializeError(e.to_string()))?;
self.metadata = Some(metadata);
Ok(())
}
fn read_project(&mut self) -> Result<(), DawprojectReadError> {
let mut project_xml = self.zip.by_name(PROJECT_PATH)?;
let mut project_xml_string = String::new();
project_xml.read_to_string(&mut project_xml_string)?;
let fixed_project_xml =
project_xml_string.replace(PROJECT_CONTENT_TYPE, PROJECT_FIXED_CONTENT_TYPE);
let project: Project = quick_xml::de::from_str(&fixed_project_xml)
.map_err(|e| DawprojectReadError::ProjectDeserializeError(e.to_string()))?;
self.project = Some(project);
Ok(())
}
pub fn read_dawproject(&mut self) -> Result<(), DawprojectReadError> {
self.read_metadata()?;
self.read_project()?;
Ok(())
}
pub fn file_names(&self) -> impl Iterator<Item = &str> {
self.file_names.iter().map(|s| s.as_str())
}
pub fn build_dawproject(&mut self) -> Option<Dawproject> {
Some(Dawproject::new(self.metadata.take()?, self.project.take()?))
}
pub fn build_dawproject_with_zip(mut self) -> Option<DawprojectWithZip<R>> {
if let Some(dawproject) = self.build_dawproject() {
return Some(DawprojectWithZip::new(dawproject, self.zip));
}
None
}
pub fn by_name(
&mut self,
name: &str,
) -> Result<zip::read::ZipFile<'_, R>, DawprojectReadError> {
self.zip
.by_name(name)
.map_err(DawprojectReadError::ZipError)
}
pub fn extract<P: AsRef<Path>>(&mut self, directory: P) -> Result<(), DawprojectReadError> {
self.zip
.extract(directory)
.map_err(DawprojectReadError::ZipError)
}
}
impl DawprojectReader<BufReader<File>> {
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, DawprojectReadError> {
let f = File::open(path).map_err(DawprojectReadError::StdIoError)?;
let reader = BufReader::new(f);
Self::new(reader)
}
}