pub mod gldf;
pub use gldf::*;
#[cfg(test)]
mod tests;
use std::fs::File as StdFile;
use std::path::PathBuf;
use std::io::Read;
use std::path::Path;
use serde_json::from_str as serde_from_str;
use zip::ZipArchive;
use anyhow::{Context, Result};
use regex::Regex;
impl GldfProduct {
pub fn detach(&mut self) -> anyhow::Result<()> {
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct BufFile {
pub name: Option<String>,
pub content: Option<Vec<u8>>,
pub file_id: Option<String>,
pub path: Option<String>,
}
pub struct FileBufGldf {
pub files: Vec<BufFile>,
pub gldf: GldfProduct,
}
impl GldfProduct {
pub fn load_gldf_file_str(&self, path: String) -> anyhow::Result<String> {
let zipfile = StdFile::open(Path::new(&self.path))?;
let mut zip = ZipArchive::new(zipfile)?;
let mut some_str = String::new();
let mut some_file = zip.by_name(&path)?;
some_file.read_to_string(&mut some_str)?;
Ok(some_str)
}
pub fn load_gldf_file(&self, path: String) -> anyhow::Result<Vec<u8>> {
let zipfile = StdFile::open(Path::new(&self.path))?;
let mut zip = ZipArchive::new(zipfile)?;
let mut file_buf = Vec::new();
let mut some_file = zip.by_name(&path)?;
some_file.read_to_end(&mut file_buf)?;
Ok(file_buf)
}
pub fn get_xml_str_from_gldf(path: PathBuf) -> anyhow::Result<String> {
let zipfile = StdFile::open(path)?;
let mut zip = ZipArchive::new(zipfile)?;
let mut xmlfile = zip.by_name("product.xml")?;
let mut xml_str = String::new();
xmlfile.read_to_string(&mut xml_str)?;
Ok(xml_str)
}
pub fn remove_bom(s: &str) -> String {
if s.starts_with("\u{FEFF}") {
s[3..].to_string()
} else {
s.to_string()
}
}
pub fn sanitize_xml_str(xml_str: &str) -> String {
let cleaned_str = Self::remove_bom(xml_str);
let re = Regex::new(r"<Root .*?>").unwrap();
re.replace_all(&cleaned_str, "<Root>").to_string()
}
pub fn from_xml(xml_str: &str) -> anyhow::Result<GldfProduct> {
let my_xml_str = Self::sanitize_xml_str(xml_str);
let result: GldfProduct = quick_xml::de::from_str(&my_xml_str)
.map_err(|e| anyhow::anyhow!("XML parsing error: {}", e))?;
Ok(result)
}
pub fn load_gldf(path: &str) -> anyhow::Result<GldfProduct> {
let path_buf = Path::new(path).to_path_buf();
let xml_str = GldfProduct::get_xml_str_from_gldf(path_buf)
.map_err(anyhow::Error::msg)
.context("Failed to parse XML string")?;
let mut loaded: GldfProduct = GldfProduct::from_xml(&xml_str)?;
loaded.path = path.to_string();
Ok(loaded)
}
pub fn load_gldf_from_buf_all(gldf_buf: Vec<u8>) -> anyhow::Result<FileBufGldf> {
let zip_buf = std::io::Cursor::new(gldf_buf);
let mut zip = ZipArchive::new(zip_buf)
.context("Failed to open GLDF as ZIP archive")?;
let mut file_bufs: Vec<BufFile> = Vec::new();
let mut xmlfile = zip.by_name("product.xml")
.context("product.xml not found in GLDF archive")?;
let mut xml_str = String::new();
xmlfile.read_to_string(&mut xml_str)
.context("Failed to read product.xml")?;
let loaded: GldfProduct = GldfProduct::from_xml(&xml_str)
.context("Failed to parse product.xml")?;
drop(xmlfile);
for i in 0..zip.len() {
if let Ok(mut file) = zip.by_index(i) {
if file.is_file() {
let mut buf: Vec<u8> = Vec::new();
if file.read_to_end(&mut buf).is_ok() {
let buf_file = BufFile {
name: Some(file.name().to_string()),
content: Some(buf),
file_id: None,
path: Some(file.name().to_string()),
};
file_bufs.push(buf_file);
}
}
}
}
let file_buf = FileBufGldf {
files: file_bufs,
gldf: loaded,
};
Ok(file_buf)
}
pub fn load_gldf_from_buf(file_buf: Vec<u8>) -> anyhow::Result<GldfProduct> {
let zip_buf = std::io::Cursor::new(file_buf);
let mut zip = ZipArchive::new(zip_buf)
.context("Failed to open GLDF as ZIP archive")?;
let mut xmlfile = zip.by_name("product.xml")
.context("product.xml not found in GLDF archive")?;
let mut xml_str = String::new();
xmlfile.read_to_string(&mut xml_str)
.context("Failed to read product.xml")?;
let loaded: GldfProduct = GldfProduct::from_xml(&xml_str)
.context("Failed to parse product.xml")?;
Ok(loaded)
}
pub fn to_json(&self) -> anyhow::Result<String> {
let json_str = serde_json::to_string(&self)?;
Ok(json_str)
}
pub fn to_pretty_json(&self) -> anyhow::Result<String> {
let json_str = serde_json::to_string_pretty(&self)
.context("Failed to serialize to JSON")?;
Ok(json_str)
}
pub fn from_json(json_str: &str) -> anyhow::Result<GldfProduct> {
let j_loaded: GldfProduct = serde_from_str(json_str)?;
Ok(j_loaded)
}
pub fn from_json_file(path: PathBuf) -> anyhow::Result<GldfProduct> {
let mut json_file = StdFile::open(&path)
.with_context(|| format!("Failed to open JSON file: {:?}", path))?;
let mut json_str = String::new();
json_file.read_to_string(&mut json_str)
.context("Failed to read JSON file")?;
GldfProduct::from_json(&json_str)
.context("Failed to parse JSON content")
}
pub fn to_xml(&self) -> anyhow::Result<String> {
let xml_str = quick_xml::se::to_string(&self)?;
Ok(format!(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n{}",
xml_str
))
}
pub fn get_phot_files(&self) -> anyhow::Result<Vec<&File>> {
let mut result: Vec<&File> = Vec::new();
for f in self.general_definitions.files.file.iter() {
let content_type = &f.content_type;
if content_type.starts_with("ldc") {
result.push(f)
}
}
Ok(result.to_owned())
}
pub fn get_image_def_files(&self) -> Result<Vec<&File>> {
let mut result: Vec<&File> = Vec::new();
for f in self.general_definitions.files.file.iter() {
let content_type = &f.content_type;
if content_type.starts_with("image") {
result.push(f)
}
}
Ok(result.to_owned())
}
pub fn get_image_zip_files(&self) -> anyhow::Result<Vec<&File>> {
let mut result: Vec<&File> = Vec::new();
for f in self.general_definitions.files.file.iter() {
let content_type = &f.content_type;
if content_type.starts_with("image") {
result.push(f)
}
}
Ok(result.to_owned())
}
#[cfg(feature = "http")]
pub async fn get_ldc_by_id(&self, file_id: String) -> Result<String, anyhow::Error> {
let mut result: String = "".to_owned();
for f in self.general_definitions.files.file.iter() {
if f.id == file_id {
let mut ldc_path = "ldc/".to_owned();
let file_name = f.file_name.to_owned();
if f.type_attr == "url" {
let tmp = fetch_text_from_url(&file_name).await?;
result.push_str(tmp.as_str());
} else {
ldc_path.push_str(&file_name);
result.push_str(&self.load_gldf_file_str(ldc_path)?);
}
}
}
Ok(result)
}
#[cfg(not(feature = "http"))]
pub fn get_ldc_by_id(&self, file_id: String) -> Result<String, anyhow::Error> {
let mut result: String = "".to_owned();
for f in self.general_definitions.files.file.iter() {
if f.id == file_id {
if f.type_attr == "url" {
return Err(anyhow::anyhow!("URL file type not supported without http feature"));
}
let mut ldc_path = "ldc/".to_owned();
ldc_path.push_str(&f.file_name);
result.push_str(&self.load_gldf_file_str(ldc_path)?);
}
}
Ok(result)
}
pub fn get_all_file_definitions(&self) -> anyhow::Result<Vec<File>> {
let mut result: Vec<File> = Vec::new();
for f in self.general_definitions.files.file.iter() {
result.push(f.to_owned());
}
Ok(result)
}
pub fn get_url_file_definitions(&self) -> anyhow::Result<Vec<File>> {
let mut result: Vec<File> = Vec::new();
for f in self.general_definitions.files.file.iter() {
if f.content_type == "url" {
result.push(f.to_owned());
}
}
Ok(result)
}
}
#[cfg(feature = "http")]
pub async fn fetch_text_from_url(url: &str) -> Result<String, reqwest::Error> {
let response = reqwest::get(url).await?;
let text = response.text().await?;
Ok(text)
}
#[cfg(feature = "http")]
pub async fn fetch_content_from_url(url: &str) -> Result<Vec<u8>, reqwest::Error> {
let response = reqwest::get(url).await?;
let content = response.bytes().await?;
Ok(content.to_vec())
}