Skip to content

Commit

Permalink
Fix broken resource code
Browse files Browse the repository at this point in the history
  • Loading branch information
snpefk committed Sep 28, 2024
1 parent 6e10ada commit 8bdc594
Showing 1 changed file with 112 additions and 73 deletions.
185 changes: 112 additions & 73 deletions crates/opensi-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
#![allow(dead_code)]

use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
use quick_xml::de::from_str;
use serde::{Deserialize, Serialize};
use std::io::ErrorKind;
use std::collections::HashMap;
use std::fmt::Display;

Check failure on line 5 in crates/opensi-core/src/lib.rs

View workflow job for this annotation

GitHub Actions / Test Suite

unused import: `std::fmt::Display`
use std::io::{Error, ErrorKind, Write};
use std::path::Path;
use std::{fs::File, io, io::Read};
use zip::write::FileOptions;
use zip::{CompressionMethod, ZipArchive, ZipWriter};

#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
#[serde(default)]
Expand All @@ -21,6 +24,10 @@ pub struct Package {
pub rounds: Rounds,
pub tags: Vec<String>,
pub info: Info,

// resources
#[serde(skip)]
pub resource: HashMap<Resource, Vec<u8>>,
}

impl Package {
Expand Down Expand Up @@ -322,20 +329,30 @@ pub struct Atom {
pub body: Option<String>,
}

#[derive(Debug)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Resource {
Audio(std::path::PathBuf),
Video(std::path::PathBuf),
Image(std::path::PathBuf),
Audio(String),
Video(String),
Image(String),
Texts(String),
}

use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
use zip::ZipArchive;
impl Resource {
fn extract_key(&self) -> &str {

Check failure on line 341 in crates/opensi-core/src/lib.rs

View workflow job for this annotation

GitHub Actions / Test Suite

method `extract_key` is never used
match self {
Resource::Audio(key) |
Resource::Video(key) |
Resource::Image(key) |
Resource::Texts(key) => key
}
}
}

const CONTROLS_ASCII_SET: &AsciiSet = &CONTROLS.add(b' ');
impl Package {
const CONTENT_TYPE_FILE_CONTENT: &'static str = r#"<?xml version="1.0" encoding="utf-8"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="xml" ContentType="si/xml" /></Types>"""#;

Check failure on line 352 in crates/opensi-core/src/lib.rs

View workflow job for this annotation

GitHub Actions / Test Suite

associated items `CONTENT_TYPE_FILE_CONTENT` and `to_bytes` are never used
const CONTROLS_ASCII_SET: &'static AsciiSet = &CONTROLS.add(b' ');

impl Atom {
pub fn get_resource(&self, filename: &str) -> Option<Resource> {
pub fn get_resource(&self, atom: &Atom) -> Option<&Vec<u8>> {
// Atom xml content or ("resource name" as stated in official docs) begins
// with '@' in package to distinguish plain text and links to
// resources. This is how it looks like in package:
Expand All @@ -348,21 +365,23 @@ impl Atom {
// beginning to make our life easier while working with files from the pack.
// It also percent-encoded so we need to decode links.

let body = self.body.as_ref()?;
let resource_name = &utf8_percent_encode(body, CONTROLS_ASCII_SET).to_string();
let tmp = std::env::temp_dir().join(filename);
let variant: &str = self.variant.as_ref()?;
let body = atom.body.as_ref()?;
let resource_name = utf8_percent_encode(body, Self::CONTROLS_ASCII_SET).to_string();
let variant: &str = atom.variant.as_ref()?;

match variant {
"voice" => Some(Resource::Audio(tmp.join("Audio").join(resource_name))),
"image" => Some(Resource::Image(tmp.join("Images").join(resource_name))),
"video" => Some(Resource::Video(tmp.join("Video").join(resource_name))),
let key = match variant {
"voice" => Some(Resource::Audio(format!("Audio/{}", resource_name))),
"image" => Some(Resource::Image(format!("Images/{}", resource_name))),
"video" => Some(Resource::Video(format!("Video/{}", resource_name))),
_ => None,
};

match key {
Some(key) => self.resource.get(&key),
None => None,
}
}
}

impl Package {
// Expecting byte array of zip file
pub fn from_zip_buffer(bytes: impl AsRef<[u8]>) -> Result<Package, io::Error> {
let cursor = io::Cursor::new(bytes);
Expand All @@ -376,60 +395,80 @@ impl Package {

fn get_package_from_zip<T: Read + io::Seek>(source: T) -> Result<Package, io::Error> {
let mut zip_archive = ZipArchive::new(source)?;
let mut resources = HashMap::new();

for i in 0..zip_archive.len() {
let mut zip_file = zip_archive.by_index(i)?;
if zip_file.is_dir() {
continue;
}

if let Some(filename) = zip_file.enclosed_name() {
if let Some(filename) = filename.to_str() {
if filename == "content.xml" || filename == "[Content_Types].xml" {
continue;
}

match Self::get_resource_type(filename) {
Ok(key) => {
let mut value = Vec::new();
zip_file.read_to_end(&mut value)?;

resources.insert(key, value);
},
Err(_) => {
println!("Unknown resource type for {}", filename)
},
}
}
}
}

let mut xml = zip_archive.by_name("content.xml")?;
let mut content_file = zip_archive.by_name("content.xml")?;
let mut contents = String::new();
xml.read_to_string(&mut contents)?;
content_file.read_to_string(&mut contents)?;

let package = from_str(&contents).map_err(|e| Error::new(ErrorKind::InvalidData, e));

package.map(|p| Package { resource: resources, ..p })
}

from_str(&contents).map_err(|e| io::Error::new(ErrorKind::InvalidData, e))
fn get_resource_type(filename: &str) -> Result<Resource, Error> {
if filename.starts_with("Audio") {
Ok(Resource::Audio(filename.to_owned()))
} else if filename.starts_with("Images") {
Ok(Resource::Image(filename.to_owned()))
} else if filename.starts_with("Video") {
Ok(Resource::Video(filename.to_owned()))
} else if filename.starts_with("Texts") {
Ok(Resource::Texts(filename.to_owned()))
} else {
Err(Error::new(ErrorKind::InvalidData, "Unknown resource type"))
}
}

// TODO: figure out how to do extraction on wasm
// pub async fn open_with_extraction(path: impl AsRef<Path>) -> Result<Package, std::io::Error> {
// let package_name = path.as_ref().file_name().unwrap().to_str().unwrap();
// let package_file = File::open(&path)?;
// let mut zip = zip::ZipArchive::new(package_file)?;

// let xml_path = Self::extract_package(package_name, &mut zip)?;
// let xml = std::fs::File::open(xml_path)?;
// let reader = std::io::BufReader::new(xml);
// from_reader(reader).map_err(|e| std::io::Error::new(ErrorKind::InvalidData, e))
// }

// /// Extract package internals into `/tmp/{package_name}/`. Return `PathBuf`
// /// to `content.xml`.
// ///
// /// # Arguments
// /// * `package_name` - package name to which the package will be extracted
// /// * `zip` - unpacked archive
// async fn extract_package(
// package_name: &str,
// zip: &mut zip::ZipArchive<File>,
// ) -> Result<std::path::PathBuf, std::io::Error> {
// let tmp = std::env::temp_dir().join(package_name);

// for i in 0..zip.len() {
// let mut zipfile = zip.by_index(i)?;
// let mut zipfile_path = zipfile.sanitized_name();
// let encoded_name = zipfile_path.file_name().unwrap().to_str().unwrap().to_string();

// if encoded_name.starts_with("@") {
// zipfile_path.set_file_name(&encoded_name[1..]);
// } else {
// zipfile_path.set_file_name(encoded_name)
// };

// if let Some(parent) = zipfile_path.parent() {
// let parent_path = tmp.join(parent);
// if !parent.exists() {
// std::fs::create_dir_all(&parent_path)?;
// }
// }

// let path = &tmp.join(zipfile_path);
// let mut fsfile = std::fs::File::create(&path)?;
// std::io::copy(&mut zipfile, &mut fsfile)?;
// }
// Ok(tmp.join("content.xml"))
// }
fn to_bytes(self) -> Result<Vec<u8>, Error> {
let buffer = Vec::new();
let cursor = io::Cursor::new(buffer);
let mut zip = ZipWriter::new(cursor);

// Define file options (e.g., compression method)
let options = FileOptions::default().compression_method(CompressionMethod::Deflated);
let xml = quick_xml::se::to_string(&self)
.map_err(|e| io::Error::new(ErrorKind::InvalidData, e))?;
zip.start_file("content.xml", options)?;
zip.write_all(&xml.into_bytes())?;

zip.start_file("[Content_Types].xml", options)?;
zip.write_all(Self::CONTENT_TYPE_FILE_CONTENT.as_ref())?;

for (key, value) in self.resource.into_iter() {
zip.start_file(key.extract_key(), options)?;
zip.write_all(&value)?
}

let result = zip.finish()?;

Ok(result.into_inner())
}
}

0 comments on commit 8bdc594

Please sign in to comment.