changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > core / rust/lib/flate/src/lib.rs

changeset 698: 96958d3eb5b0
parent: 3d78bed56188
author: Richard Westhaver <ellis@rwest.io>
date: Fri, 04 Oct 2024 22:04:59 -0400
permissions: -rw-r--r--
description: fixes
1 //! flate compression/archival modules
2 //!
3 //! This library provides wrappers for compression and archive
4 //! libraries. Currently only zstd and tar are supported, but will
5 //! soon support a wider variety of backends.
6 //!
7 //! Backends will be conditionally-compiled based on feature flags,
8 //! and will rely on C bindings as little as possible, allowing for
9 //! more flexibility in platform support.
10 use std::{fs, io, path::Path};
11 
12 pub use tar;
13 
14 /// Level of compression data should be compressed with.
15 #[non_exhaustive]
16 #[derive(Clone, Copy, Debug)]
17 pub enum Level {
18  /// Fastest quality of compression, usually produces bigger size.
19  Fastest,
20  /// Best quality of compression, usually produces the smallest size.
21  Best,
22  /// Default quality of compression defined by the selected compression
23  /// algorithm.
24  Default,
25  /// Precise quality based on the underlying compression algorithms'
26  /// qualities. The interpretation of this depends on the algorithm chosen
27  /// and the specific implementation backing it.
28  /// Qualities are implicitly clamped to the algorithm's maximum.
29  Precise(u32),
30 }
31 
32 impl Level {
33  fn into_zstd(self) -> i32 {
34  match self {
35  Self::Fastest => 1,
36  Self::Best => 21,
37  Self::Precise(quality) => quality.min(21) as i32,
38  Self::Default => 0,
39  }
40  }
41 }
42 
43 /// Pack a SRC directory, and return a compressed archive at DST.
44 pub fn pack<P: AsRef<Path>, Q: AsRef<Path>>(
45  src: P,
46  dst: Q,
47  level: Option<Level>,
48 ) {
49  let mut tar = tar::Builder::new(Vec::new());
50  let src = src.as_ref();
51  let parent = src.parent().unwrap();
52  let art = src.strip_prefix(parent).unwrap();
53  tar.append_dir_all(art, src).unwrap();
54 
55  let tar = tar.into_inner().unwrap();
56  let dst = dst.as_ref();
57  let file = fs::File::create(dst).expect("failed to create output path");
58  zstd::stream::copy_encode(
59  &tar[..],
60  file,
61  level.unwrap_or(Level::Best).into_zstd(),
62  )
63  .unwrap();
64 }
65 
66 /// unpack a tar.zst compressed archive or zst file
67 pub fn unpack<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) {
68  let src = src.as_ref();
69  let input = fs::File::open(src).expect("failed to open input");
70  let mut buff = Vec::new();
71  zstd::stream::copy_decode(input, &mut buff).unwrap();
72  if tar::Archive::new(&buff[..]).entries().is_ok() {
73  tar::Archive::new(&buff[..]).unpack(dst).unwrap();
74  } else {
75  decompress(src).unwrap();
76  }
77 }
78 
79 /// unpack a tar.zst compressed archive, removing the source file before
80 /// returning
81 pub fn unpack_replace<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) {
82  unpack(&src, dst);
83  fs::remove_file(src).expect("could not remove source package");
84 }
85 
86 /// compress a file with zstd
87 pub fn compress<P: AsRef<Path>, Q: AsRef<Path>>(
88  src: P,
89  dst: Q,
90 ) -> io::Result<()> {
91  let mut file = fs::File::open(&src)?;
92  let mut encoder = {
93  let target = fs::File::create(dst.as_ref())?;
94  zstd::Encoder::new(target, 22)?
95  };
96  io::copy(&mut file, &mut encoder)?;
97  encoder.finish()?;
98  Ok(())
99 }
100 
101 /// decompress a zst file into the current directory
102 pub fn decompress<P: AsRef<Path>>(source: P) -> io::Result<()> {
103  let mut decoder = {
104  let file = fs::File::open(&source)?;
105  zstd::Decoder::new(file)?
106  };
107  let mut target = fs::File::create(source.as_ref().to_str().unwrap())?;
108  io::copy(&mut decoder, &mut target)?;
109  decoder.finish();
110  Ok(())
111 }
112 
113 #[test]
114 fn pack_test() {
115  let dir_path = Path::new("pack_test");
116 
117  std::fs::create_dir(dir_path).unwrap();
118 
119  for i in 0..10 {
120  std::fs::File::create(&dir_path.join(format!("{}.test", i))).unwrap();
121  }
122 
123  pack(dir_path, "pack_test.tar.zst", None);
124  unpack("pack_test.tar.zst", "pack_test");
125  unpack_replace("pack_test.tar.zst", "pack_test");
126 
127  std::fs::remove_dir_all(dir_path).unwrap();
128 }