-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add more methods used in cargo-dist
#38
Changes from all commits
9f68e92
8d8dc5b
089bd56
72c0352
dcc3954
d3a61f9
893c12b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
//! Compression-related methods, all used in `axoasset::Local` | ||
|
||
use crate::error::*; | ||
use camino::Utf8Path; | ||
use flate2::{write::ZlibEncoder, Compression, GzBuilder}; | ||
use std::{ | ||
fs::{self, DirEntry}, | ||
io::BufReader, | ||
}; | ||
use xz2::write::XzEncoder; | ||
use zip::ZipWriter; | ||
|
||
/// Internal tar-file compression algorithms | ||
#[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
pub(crate) enum CompressionImpl { | ||
/// .gz | ||
Gzip, | ||
/// .xz | ||
Xzip, | ||
/// .zstd | ||
Zstd, | ||
} | ||
|
||
pub(crate) fn tar_dir( | ||
src_path: &Utf8Path, | ||
dest_path: &Utf8Path, | ||
compression: &CompressionImpl, | ||
) -> Result<()> { | ||
// Set up the archive/compression | ||
// The contents of the zip (e.g. a tar) | ||
let dir_name = src_path.file_name().unwrap(); | ||
let zip_contents_name = format!("{dir_name}.tar"); | ||
let final_zip_file = match fs::File::create(dest_path) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this a spot where we could use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure, since we're just creating the file and not initially writing anything to it, before passing it off to the various buffer writers There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think we could- and it would help with debugging as the error that throws is the write new error and so using the associated function i think makes sense There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmmm, if anything, i'd rather add a new There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ahhh got it, sorry, haven't had any coffee yet today lol. i like the write and open idea but we could file that as an issue and not block on it |
||
Ok(file) => file, | ||
Err(details) => { | ||
return Err(AxoassetError::LocalAssetWriteNewFailed { | ||
dest_path: dest_path.to_string(), | ||
details, | ||
}) | ||
} | ||
}; | ||
|
||
match compression { | ||
CompressionImpl::Gzip => { | ||
// Wrap our file in compression | ||
let zip_output = GzBuilder::new() | ||
.filename(zip_contents_name) | ||
.write(final_zip_file, Compression::default()); | ||
|
||
// Write the tar to the compression stream | ||
let mut tar = tar::Builder::new(zip_output); | ||
|
||
// Add the whole dir to the tar | ||
if let Err(details) = tar.append_dir_all(dir_name, src_path) { | ||
return Err(AxoassetError::LocalAssetArchive { | ||
reason: format!("failed to copy directory into tar: {src_path} => {dir_name}",), | ||
details, | ||
}); | ||
} | ||
// Finish up the tarring | ||
let zip_output = match tar.into_inner() { | ||
Ok(out) => out, | ||
Err(details) => { | ||
return Err(AxoassetError::LocalAssetArchive { | ||
reason: format!("failed to write tar: {dest_path}"), | ||
details, | ||
}) | ||
} | ||
}; | ||
// Finish up the compression | ||
let _zip_file = match zip_output.finish() { | ||
Ok(file) => file, | ||
Err(details) => { | ||
return Err(AxoassetError::LocalAssetArchive { | ||
reason: format!("failed to write archive: {dest_path}"), | ||
details, | ||
}) | ||
} | ||
}; | ||
// Drop the file to close it | ||
} | ||
CompressionImpl::Xzip => { | ||
let zip_output = XzEncoder::new(final_zip_file, 9); | ||
// Write the tar to the compression stream | ||
let mut tar = tar::Builder::new(zip_output); | ||
|
||
// Add the whole dir to the tar | ||
if let Err(details) = tar.append_dir_all(dir_name, src_path) { | ||
return Err(AxoassetError::LocalAssetArchive { | ||
reason: format!("failed to copy directory into tar: {src_path} => {dir_name}",), | ||
details, | ||
}); | ||
} | ||
// Finish up the tarring | ||
let zip_output = match tar.into_inner() { | ||
Ok(out) => out, | ||
Err(details) => { | ||
return Err(AxoassetError::LocalAssetArchive { | ||
reason: format!("failed to write tar: {dest_path}"), | ||
details, | ||
}) | ||
} | ||
}; | ||
// Finish up the compression | ||
let _zip_file = match zip_output.finish() { | ||
Ok(file) => file, | ||
Err(details) => { | ||
return Err(AxoassetError::LocalAssetArchive { | ||
reason: format!("failed to write archive: {dest_path}"), | ||
details, | ||
}) | ||
} | ||
}; | ||
// Drop the file to close it | ||
} | ||
CompressionImpl::Zstd => { | ||
// Wrap our file in compression | ||
let zip_output = ZlibEncoder::new(final_zip_file, Compression::default()); | ||
|
||
// Write the tar to the compression stream | ||
let mut tar = tar::Builder::new(zip_output); | ||
|
||
// Add the whole dir to the tar | ||
if let Err(details) = tar.append_dir_all(dir_name, src_path) { | ||
return Err(AxoassetError::LocalAssetArchive { | ||
reason: format!("failed to copy directory into tar: {src_path} => {dir_name}",), | ||
details, | ||
}); | ||
} | ||
// Finish up the tarring | ||
let zip_output = match tar.into_inner() { | ||
Ok(out) => out, | ||
Err(details) => { | ||
return Err(AxoassetError::LocalAssetArchive { | ||
reason: format!("failed to write tar: {dest_path}"), | ||
details, | ||
}) | ||
} | ||
}; | ||
// Finish up the compression | ||
let _zip_file = match zip_output.finish() { | ||
Ok(file) => file, | ||
Err(details) => { | ||
return Err(AxoassetError::LocalAssetArchive { | ||
reason: format!("failed to write archive: {dest_path}"), | ||
details, | ||
}) | ||
} | ||
}; | ||
// Drop the file to close it | ||
} | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
pub(crate) fn zip_dir(src_path: &Utf8Path, dest_path: &Utf8Path) -> Result<()> { | ||
// Set up the archive/compression | ||
let final_zip_file = match fs::File::create(dest_path) { | ||
Ok(file) => file, | ||
Err(details) => { | ||
return Err(AxoassetError::LocalAssetWriteNewFailed { | ||
dest_path: dest_path.to_string(), | ||
details, | ||
}) | ||
} | ||
}; | ||
|
||
// Wrap our file in compression | ||
let mut zip = ZipWriter::new(final_zip_file); | ||
|
||
let dir = match std::fs::read_dir(src_path) { | ||
Ok(dir) => dir, | ||
Err(details) => { | ||
return Err(AxoassetError::LocalAssetReadFailed { | ||
origin_path: src_path.to_string(), | ||
details, | ||
}) | ||
} | ||
}; | ||
|
||
for entry in dir { | ||
if let Err(details) = copy_into_zip(entry, &mut zip) { | ||
return Err(AxoassetError::LocalAssetArchive { | ||
reason: format!("failed to create file in zip: {dest_path}"), | ||
details, | ||
}); | ||
} | ||
} | ||
|
||
// Finish up the compression | ||
let _zip_file = match zip.finish() { | ||
Ok(file) => file, | ||
Err(details) => { | ||
return Err(AxoassetError::LocalAssetArchive { | ||
reason: format!("failed to write archive: {dest_path}"), | ||
details: details.into(), | ||
}) | ||
} | ||
}; | ||
// Drop the file to close it | ||
Ok(()) | ||
} | ||
|
||
/// Copies a file into a provided `ZipWriter`. Mostly factored out so that we can bunch up | ||
/// a bunch of `std::io::Error`s without having to individually handle them. | ||
fn copy_into_zip( | ||
entry: std::result::Result<DirEntry, std::io::Error>, | ||
zip: &mut ZipWriter<fs::File>, | ||
) -> std::result::Result<(), std::io::Error> { | ||
shadows-withal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let entry = entry?; | ||
if entry.file_type()?.is_file() { | ||
let options = | ||
zip::write::FileOptions::default().compression_method(zip::CompressionMethod::Stored); | ||
let file = fs::File::open(entry.path())?; | ||
let mut buf = BufReader::new(file); | ||
let file_name = entry.file_name(); | ||
// FIXME: ...don't do this lossy conversion? | ||
let utf8_file_name = file_name.to_string_lossy(); | ||
zip.start_file(utf8_file_name.clone(), options)?; | ||
std::io::copy(&mut buf, zip)?; | ||
} else { | ||
todo!("implement zip subdirs! (or was this a symlink?)"); | ||
shadows-withal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
Ok(()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I figured it'd be best to expose all possible methods, while at the same time keeping a crate-internal duplicate of
cargo-dist
sCompressionImpl
so we can carry over the function code mostly unmodified (with the exception of the error handling)