use crate::{
errors::ZipFileError,
imports::IMPORTS_DIRECTORY_NAME,
inputs::{INPUTS_DIRECTORY_NAME, INPUT_FILE_EXTENSION, STATE_FILE_EXTENSION},
outputs::{
CHECKSUM_FILE_EXTENSION,
CIRCUIT_FILE_EXTENSION,
OUTPUTS_DIRECTORY_NAME,
PROOF_FILE_EXTENSION,
PROVING_KEY_FILE_EXTENSION,
VERIFICATION_KEY_FILE_EXTENSION,
},
root::{MANIFEST_FILENAME, README_FILENAME},
source::{SOURCE_DIRECTORY_NAME, SOURCE_FILE_EXTENSION},
};
use serde::Deserialize;
use std::{
borrow::Cow,
fs::{
File,
{self},
},
io::{Read, Write},
path::Path,
};
use walkdir::WalkDir;
use zip::write::{FileOptions, ZipWriter};
pub static ZIP_FILE_EXTENSION: &str = ".zip";
#[derive(Deserialize)]
pub struct ZipFile {
pub package_name: String,
}
impl ZipFile {
pub fn new(package_name: &str) -> Self {
Self {
package_name: package_name.to_string(),
}
}
pub fn exists_at(&self, path: &Path) -> bool {
let path = self.setup_file_path(path);
path.exists()
}
pub fn get_file_path<'a>(&self, current_dir: &'a Path) -> Cow<'a, Path> {
self.setup_file_path(current_dir)
}
pub fn write(&self, src_dir: &Path) -> Result<(), ZipFileError> {
let walkdir = WalkDir::new(src_dir);
let path = self.setup_file_path(src_dir);
let file = &mut File::create(&path)?;
let mut zip = ZipWriter::new(file);
let options = FileOptions::default()
.compression_method(zip::CompressionMethod::Stored)
.unix_permissions(0o755);
let mut buffer = Vec::new();
for entry in walkdir.into_iter().filter_map(|e| e.ok()) {
let path = entry.path();
let name = path.strip_prefix(src_dir).unwrap();
let included = is_included(name);
tracing::debug!("Checking if {:?} is included - {}", name, included);
if !included {
continue;
}
if path.is_file() {
tracing::info!("Adding file {:?} as {:?}", path, name);
#[allow(deprecated)]
zip.start_file_from_path(name, options)?;
let mut f = File::open(path)?;
f.read_to_end(&mut buffer)?;
zip.write_all(&*buffer)?;
buffer.clear();
} else if !name.as_os_str().is_empty() {
tracing::info!("Adding directory {:?} as {:?}", path, name);
#[allow(deprecated)]
zip.add_directory_from_path(name, options)?;
}
}
zip.finish()?;
tracing::info!("Package zip file created successfully {:?}", path);
Ok(())
}
pub fn remove(&self, path: &Path) -> Result<bool, ZipFileError> {
let path = self.setup_file_path(path);
if !path.exists() {
return Ok(false);
}
fs::remove_file(&path).map_err(|_| ZipFileError::FileRemovalError(path.into_owned()))?;
Ok(true)
}
fn setup_file_path<'a>(&self, path: &'a Path) -> Cow<'a, Path> {
let mut path = Cow::from(path);
if path.is_dir() {
if !path.ends_with(OUTPUTS_DIRECTORY_NAME) {
path.to_mut().push(OUTPUTS_DIRECTORY_NAME);
}
path.to_mut()
.push(format!("{}{}", self.package_name, ZIP_FILE_EXTENSION));
}
path
}
}
fn is_included(path: &Path) -> bool {
if path.ends_with(OUTPUTS_DIRECTORY_NAME.trim_end_matches('/'))
| path.ends_with(IMPORTS_DIRECTORY_NAME.trim_end_matches('/'))
{
return false;
}
if let Some(true) = path.extension().map(|ext| {
ext.eq(ZIP_FILE_EXTENSION.trim_start_matches('.'))
| ext.eq(PROVING_KEY_FILE_EXTENSION.trim_start_matches('.'))
| ext.eq(VERIFICATION_KEY_FILE_EXTENSION.trim_start_matches('.'))
| ext.eq(PROOF_FILE_EXTENSION.trim_start_matches('.'))
| ext.eq(CHECKSUM_FILE_EXTENSION.trim_start_matches('.'))
| ext.eq(ZIP_FILE_EXTENSION.trim_start_matches('.'))
| ext.eq(CIRCUIT_FILE_EXTENSION.trim_start_matches('.'))
}) {
return false;
}
if path.ends_with(INPUTS_DIRECTORY_NAME.trim_end_matches('/')) {
return true;
}
if let Some(true) = path.extension().map(|ext| {
ext.eq(INPUT_FILE_EXTENSION.trim_start_matches('.')) | ext.eq(STATE_FILE_EXTENSION.trim_start_matches('.'))
}) {
return true;
}
if (path.ends_with(README_FILENAME) | path.ends_with(MANIFEST_FILENAME)) & (path.parent() == Some(Path::new(""))) {
return true;
}
if !path.starts_with(SOURCE_DIRECTORY_NAME.trim_end_matches('/')) {
return false;
}
path.extension()
.map(|ext| ext.eq(SOURCE_FILE_EXTENSION.trim_start_matches('.')))
.unwrap_or(false)
}