1use std::{
4 fmt::{Debug, Display},
5 fs::File,
6 io::Write,
7 num::TryFromIntError,
8};
9
10use alpm_types::CompressionAlgorithmFileExtension;
11use bzip2::write::BzEncoder;
12use flate2::write::GzEncoder;
13use liblzma::write::XzEncoder;
14use log::trace;
15use zstd::Encoder;
16
17#[derive(Debug, thiserror::Error)]
19pub enum Error {
20 #[error(
22 "Error creating a Zstandard encoder while {context} with {compression_settings:?}:\n{source}"
23 )]
24 CreateZstandardEncoder {
25 context: &'static str,
30 compression_settings: CompressionSettings,
32 source: std::io::Error,
34 },
35
36 #[error("Error while finishing {compression_type} compression encoder:\n{source}")]
38 FinishEncoder {
39 compression_type: CompressionAlgorithmFileExtension,
41 source: std::io::Error,
43 },
44
45 #[error("Error while trying to get available parallelism:\n{0}")]
47 GetParallelism(#[source] std::io::Error),
48
49 #[error("Error while trying to convert an integer:\n{0}")]
51 IntegerConversion(#[source] TryFromIntError),
52
53 #[error("Invalid compression level {level} (must be in the range {min} - {max})")]
55 InvalidCompressionLevel {
56 level: u8,
58 min: u8,
60 max: u8,
62 },
63}
64
65macro_rules! define_compression_level {
71 (
72 $name:ident,
73 Min => $min:expr,
74 Max => $max:expr,
75 Default => $default:expr,
76 $compression:literal,
77 $url:literal
78 ) => {
79 #[doc = concat!("Compression level for ", $compression, " compression.")]
80 #[derive(Clone, Debug, Eq, PartialEq)]
81 pub struct $name(u8);
82
83 impl $name {
84 #[doc = concat!("Creates a new [`", stringify!($name), "`] from a [`u8`].")]
85 #[doc = concat!("The `level` must be in the range of [`", stringify!($name), "::min`] and [`", stringify!($name), "::max`].")]
87 #[doc = concat!("Returns an error if the value is not in the range of [`", stringify!($name), "::min`] and [`", stringify!($name), "::max`].")]
91 pub fn new(level: u8) -> Result<Self, Error> {
92 trace!(concat!("Creating new compression level for ", $compression, " compression with {{level}}"));
93 if !($name::min()..=$name::max()).contains(&level) {
94 return Err(Error::InvalidCompressionLevel {
95 level,
96 min: $name::min(),
97 max: $name::max(),
98 });
99 }
100 Ok(Self(level))
101 }
102
103 #[doc = concat!("Returns the default level (`", stringify!($default), "`) for [`", stringify!($name), "`].")]
104 #[doc = concat!("The default level adheres to the one selected by the [", $compression, "] executable.")]
106 #[doc = concat!("[", $compression, "]: ", $url)]
108 pub const fn default_level() -> u8 {
109 $default
110 }
111
112 #[doc = concat!("Returns the minimum allowed level (`", stringify!($min), "`) for [`", stringify!($name), "`].")]
113 pub const fn min() -> u8 {
114 $min
115 }
116
117 #[doc = concat!("Returns the maximum allowed level (`", stringify!($max), "`) for [`", stringify!($name), "`].")]
118 pub const fn max() -> u8 {
119 $max
120 }
121 }
122
123 impl Default for $name {
124 #[doc = concat!("Returns the default [`", stringify!($name), "`].")]
125 #[doc = concat!("Delegates to [`", stringify!($name), "::default_level`] for retrieving the default compression level.")]
127 fn default() -> Self {
128 Self($name::default_level())
129 }
130 }
131
132 impl Display for $name {
133 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134 write!(f, "{}", self.0)
135 }
136 }
137
138 impl From<&$name> for i32 {
139 fn from(value: &$name) -> Self {
140 i32::from(value.0)
141 }
142 }
143
144 impl From<&$name> for u32 {
145 fn from(value: &$name) -> Self {
146 u32::from(value.0)
147 }
148 }
149
150 impl TryFrom<i8> for $name {
151 type Error = Error;
152
153 fn try_from(value: i8) -> Result<Self, Error> {
154 $name::new(u8::try_from(value).map_err(Error::IntegerConversion)?)
155 }
156 }
157
158 impl TryFrom<i16> for $name {
159 type Error = Error;
160
161 fn try_from(value: i16) -> Result<Self, Error> {
162 $name::new(u8::try_from(value).map_err(Error::IntegerConversion)?)
163 }
164 }
165
166 impl TryFrom<i32> for $name {
167 type Error = Error;
168
169 fn try_from(value: i32) -> Result<Self, Error> {
170 $name::new(u8::try_from(value).map_err(Error::IntegerConversion)?)
171 }
172 }
173
174 impl TryFrom<i64> for $name {
175 type Error = Error;
176
177 fn try_from(value: i64) -> Result<Self, Error> {
178 $name::new(u8::try_from(value).map_err(Error::IntegerConversion)?)
179 }
180 }
181
182 impl TryFrom<u8> for $name {
183 type Error = Error;
184
185 fn try_from(value: u8) -> Result<Self, Error> {
186 $name::new(value)
187 }
188 }
189
190 impl TryFrom<u16> for $name {
191 type Error = Error;
192
193 fn try_from(value: u16) -> Result<Self, Error> {
194 $name::new(u8::try_from(value).map_err(Error::IntegerConversion)?)
195 }
196 }
197
198 impl TryFrom<u32> for $name {
199 type Error = Error;
200
201 fn try_from(value: u32) -> Result<Self, Error> {
202 $name::new(u8::try_from(value).map_err(Error::IntegerConversion)?)
203 }
204 }
205
206 impl TryFrom<u64> for $name {
207 type Error = Error;
208
209 fn try_from(value: u64) -> Result<Self, Error> {
210 $name::new(u8::try_from(value).map_err(Error::IntegerConversion)?)
211 }
212 }
213 };
214}
215
216define_compression_level!(
218 Bzip2CompressionLevel,
219 Min => 1,
220 Max => 9,
221 Default => 9,
222 "bzip2",
223 "https://man.archlinux.org/man/bzip2.1"
224);
225
226define_compression_level!(
228 GzipCompressionLevel,
229 Min => 1,
230 Max => 9,
231 Default => 6,
232 "gzip",
233 "https://man.archlinux.org/man/gzip.1"
234);
235
236define_compression_level!(
238 XzCompressionLevel,
239 Min => 0,
240 Max => 9,
241 Default => 6,
242 "xz",
243 "https://man.archlinux.org/man/xz.1"
244);
245
246define_compression_level!(
248 ZstdCompressionLevel,
249 Min => 0,
250 Max => 22,
251 Default => 3,
252 "zstd",
253 "https://man.archlinux.org/man/zstd.1"
254);
255
256#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
263pub struct ZstdThreads(u32);
264
265impl ZstdThreads {
266 pub fn new(threads: u32) -> Self {
268 Self(threads)
269 }
270
271 pub fn all() -> Self {
275 Self(0)
276 }
277}
278
279impl Default for ZstdThreads {
280 fn default() -> Self {
286 Self(1)
287 }
288}
289
290#[derive(Clone, Debug, Eq, PartialEq)]
292pub enum CompressionSettings {
293 Bzip2 {
295 compression_level: Bzip2CompressionLevel,
297 },
298
299 Gzip {
301 compression_level: GzipCompressionLevel,
303 },
304
305 Xz {
307 compression_level: XzCompressionLevel,
309 },
310
311 Zstd {
313 compression_level: ZstdCompressionLevel,
315 threads: ZstdThreads,
317 },
318}
319
320impl Default for CompressionSettings {
321 fn default() -> Self {
327 Self::Zstd {
328 compression_level: ZstdCompressionLevel::default(),
329 threads: ZstdThreads::default(),
330 }
331 }
332}
333
334impl From<&CompressionSettings> for CompressionAlgorithmFileExtension {
335 fn from(value: &CompressionSettings) -> Self {
337 match value {
338 CompressionSettings::Bzip2 { .. } => CompressionAlgorithmFileExtension::Bzip2,
339 CompressionSettings::Gzip { .. } => CompressionAlgorithmFileExtension::Gzip,
340 CompressionSettings::Xz { .. } => CompressionAlgorithmFileExtension::Xz,
341 CompressionSettings::Zstd { .. } => CompressionAlgorithmFileExtension::Zstd,
342 }
343 }
344}
345
346fn create_zstd_encoder(
361 file: File,
362 compression_level: &ZstdCompressionLevel,
363 threads: &ZstdThreads,
364 settings: &CompressionSettings,
365) -> Result<Encoder<'static, File>, Error> {
366 let mut encoder = Encoder::new(file, compression_level.into()).map_err(|source| {
367 Error::CreateZstandardEncoder {
368 context: "initializing",
369 compression_settings: settings.clone(),
370 source,
371 }
372 })?;
373 encoder
375 .include_checksum(true)
376 .map_err(|source| Error::CreateZstandardEncoder {
377 context: "setting checksums to be added",
378 compression_settings: settings.clone(),
379 source,
380 })?;
381
382 let threads = match threads {
384 ZstdThreads(0) => {
388 u32::try_from(num_cpus::get_physical()).map_err(Error::IntegerConversion)?
389 }
390 ZstdThreads(threads) => *threads,
391 };
392
393 encoder
395 .multithread(threads)
396 .map_err(|source| Error::CreateZstandardEncoder {
397 context: "setting checksums to be added",
398 compression_settings: settings.clone(),
399 source,
400 })?;
401
402 Ok(encoder)
403}
404
405pub enum CompressionEncoder<'a> {
410 Bzip2(BzEncoder<File>),
412
413 Gzip(GzEncoder<File>),
415
416 Xz(XzEncoder<File>),
418
419 Zstd(Encoder<'a, File>),
421}
422
423impl CompressionEncoder<'_> {
424 pub fn new(file: File, settings: &CompressionSettings) -> Result<Self, Error> {
434 Ok(match settings {
435 CompressionSettings::Bzip2 { compression_level } => Self::Bzip2(BzEncoder::new(
436 file,
437 bzip2::Compression::new(compression_level.into()),
438 )),
439 CompressionSettings::Gzip { compression_level } => Self::Gzip(GzEncoder::new(
440 file,
441 flate2::Compression::new(compression_level.into()),
442 )),
443 CompressionSettings::Xz { compression_level } => {
444 Self::Xz(XzEncoder::new_parallel(file, compression_level.into()))
445 }
446 CompressionSettings::Zstd {
447 compression_level,
448 threads,
449 } => Self::Zstd(create_zstd_encoder(
450 file,
451 compression_level,
452 threads,
453 settings,
454 )?),
455 })
456 }
457
458 pub fn finish(self) -> Result<File, Error> {
464 match self {
465 CompressionEncoder::Bzip2(encoder) => {
466 encoder.finish().map_err(|source| Error::FinishEncoder {
467 compression_type: CompressionAlgorithmFileExtension::Bzip2,
468 source,
469 })
470 }
471 CompressionEncoder::Gzip(encoder) => {
472 encoder.finish().map_err(|source| Error::FinishEncoder {
473 compression_type: CompressionAlgorithmFileExtension::Gzip,
474 source,
475 })
476 }
477 CompressionEncoder::Xz(encoder) => {
478 encoder.finish().map_err(|source| Error::FinishEncoder {
479 compression_type: CompressionAlgorithmFileExtension::Xz,
480 source,
481 })
482 }
483 CompressionEncoder::Zstd(encoder) => {
484 encoder.finish().map_err(|source| Error::FinishEncoder {
485 compression_type: CompressionAlgorithmFileExtension::Zstd,
486 source,
487 })
488 }
489 }
490 }
491}
492
493impl Debug for CompressionEncoder<'_> {
494 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
495 write!(
496 f,
497 "CompressionEncoder({})",
498 match self {
499 CompressionEncoder::Bzip2(_) => "Bzip2",
500 CompressionEncoder::Gzip(_) => "Gzip",
501 CompressionEncoder::Xz(_) => "Xz",
502 CompressionEncoder::Zstd(_) => "Zstd",
503 }
504 )
505 }
506}
507
508impl Write for CompressionEncoder<'_> {
509 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
510 match self {
511 CompressionEncoder::Bzip2(encoder) => encoder.write(buf),
512 CompressionEncoder::Gzip(encoder) => encoder.write(buf),
513 CompressionEncoder::Xz(encoder) => encoder.write(buf),
514 CompressionEncoder::Zstd(encoder) => encoder.write(buf),
515 }
516 }
517
518 fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
519 match self {
520 CompressionEncoder::Bzip2(encoder) => encoder.write_vectored(bufs),
521 CompressionEncoder::Gzip(encoder) => encoder.write_vectored(bufs),
522 CompressionEncoder::Xz(encoder) => encoder.write_vectored(bufs),
523 CompressionEncoder::Zstd(encoder) => encoder.write_vectored(bufs),
524 }
525 }
526
527 fn flush(&mut self) -> std::io::Result<()> {
528 match self {
529 CompressionEncoder::Bzip2(encoder) => encoder.flush(),
530 CompressionEncoder::Gzip(encoder) => encoder.flush(),
531 CompressionEncoder::Xz(encoder) => encoder.flush(),
532 CompressionEncoder::Zstd(encoder) => encoder.flush(),
533 }
534 }
535
536 fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
537 match self {
538 CompressionEncoder::Bzip2(encoder) => encoder.write_all(buf),
539 CompressionEncoder::Gzip(encoder) => encoder.write_all(buf),
540 CompressionEncoder::Xz(encoder) => encoder.write_all(buf),
541 CompressionEncoder::Zstd(encoder) => encoder.write_all(buf),
542 }
543 }
544
545 fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> std::io::Result<()> {
546 match self {
547 CompressionEncoder::Bzip2(encoder) => encoder.write_fmt(fmt),
548 CompressionEncoder::Gzip(encoder) => encoder.write_fmt(fmt),
549 CompressionEncoder::Xz(encoder) => encoder.write_fmt(fmt),
550 CompressionEncoder::Zstd(encoder) => encoder.write_fmt(fmt),
551 }
552 }
553
554 fn by_ref(&mut self) -> &mut Self
555 where
556 Self: Sized,
557 {
558 self
559 }
560}
561
562#[cfg(test)]
563mod tests {
564 use std::io::IoSlice;
565
566 use proptest::{proptest, test_runner::Config as ProptestConfig};
567 use rstest::rstest;
568 use tempfile::tempfile;
569 use testresult::TestResult;
570
571 use super::*;
572
573 proptest! {
574 #![proptest_config(ProptestConfig::with_cases(1000))]
575
576 #[test]
577 fn valid_bzip2_compression_level_try_from_i8(input in 1..=9i8) {
578 assert!(Bzip2CompressionLevel::try_from(input).is_ok());
579 }
580
581 #[test]
582 fn valid_bzip2_compression_level_try_from_i16(input in 1..=9i16) {
583 assert!(Bzip2CompressionLevel::try_from(input).is_ok());
584 }
585
586 #[test]
587 fn valid_bzip2_compression_level_try_from_i32(input in 1..=9i32) {
588 assert!(Bzip2CompressionLevel::try_from(input).is_ok());
589 }
590
591 #[test]
592 fn valid_bzip2_compression_level_try_from_i64(input in 1..=9i64) {
593 assert!(Bzip2CompressionLevel::try_from(input).is_ok());
594 }
595
596 #[test]
597 fn valid_bzip2_compression_level_try_from_u8(input in 1..=9u8) {
598 assert!(Bzip2CompressionLevel::try_from(input).is_ok());
599 }
600
601 #[test]
602 fn valid_bzip2_compression_level_try_from_u16(input in 1..=9u16) {
603 assert!(Bzip2CompressionLevel::try_from(input).is_ok());
604 }
605
606 #[test]
607 fn valid_bzip2_compression_level_try_from_u32(input in 1..=9u32) {
608 assert!(Bzip2CompressionLevel::try_from(input).is_ok());
609 }
610
611 #[test]
612 fn valid_bzip2_compression_level_try_from_u64(input in 1..=9u64) {
613 assert!(Bzip2CompressionLevel::try_from(input).is_ok());
614 }
615
616 #[test]
617 fn valid_gzip_compression_level_try_from_i8(input in 1..=9i8) {
618 assert!(GzipCompressionLevel::try_from(input).is_ok());
619 }
620
621 #[test]
622 fn valid_gzip_compression_level_try_from_i16(input in 1..=9i16) {
623 assert!(GzipCompressionLevel::try_from(input).is_ok());
624 }
625
626 #[test]
627 fn valid_gzip_compression_level_try_from_i32(input in 1..=9i32) {
628 assert!(GzipCompressionLevel::try_from(input).is_ok());
629 }
630
631 #[test]
632 fn valid_gzip_compression_level_try_from_i64(input in 1..=9i64) {
633 assert!(GzipCompressionLevel::try_from(input).is_ok());
634 }
635
636 #[test]
637 fn valid_gzip_compression_level_try_from_u8(input in 1..=9u8) {
638 assert!(GzipCompressionLevel::try_from(input).is_ok());
639 }
640
641 #[test]
642 fn valid_gzip_compression_level_try_from_u16(input in 1..=9u16) {
643 assert!(GzipCompressionLevel::try_from(input).is_ok());
644 }
645
646 #[test]
647 fn valid_gzip_compression_level_try_from_u32(input in 1..=9u32) {
648 assert!(GzipCompressionLevel::try_from(input).is_ok());
649 }
650
651 #[test]
652 fn valid_gzip_compression_level_try_from_u64(input in 1..=9u64) {
653 assert!(GzipCompressionLevel::try_from(input).is_ok());
654 }
655
656 #[test]
657 fn valid_xz_compression_level_try_from_i8(input in 0..=9i8) {
658 assert!(XzCompressionLevel::try_from(input).is_ok());
659 }
660
661 #[test]
662 fn valid_xz_compression_level_try_from_i16(input in 0..=9i16) {
663 assert!(XzCompressionLevel::try_from(input).is_ok());
664 }
665
666 #[test]
667 fn valid_xz_compression_level_try_from_i32(input in 0..=9i32) {
668 assert!(XzCompressionLevel::try_from(input).is_ok());
669 }
670
671 #[test]
672 fn valid_xz_compression_level_try_from_i64(input in 0..=9i64) {
673 assert!(XzCompressionLevel::try_from(input).is_ok());
674 }
675
676 #[test]
677 fn valid_xz_compression_level_try_from_u8(input in 0..=9u8) {
678 assert!(XzCompressionLevel::try_from(input).is_ok());
679 }
680
681 #[test]
682 fn valid_xz_compression_level_try_from_u16(input in 0..=9u16) {
683 assert!(XzCompressionLevel::try_from(input).is_ok());
684 }
685
686 #[test]
687 fn valid_xz_compression_level_try_from_u32(input in 0..=9u32) {
688 assert!(XzCompressionLevel::try_from(input).is_ok());
689 }
690
691 #[test]
692 fn valid_xz_compression_level_try_from_u64(input in 0..=9u64) {
693 assert!(XzCompressionLevel::try_from(input).is_ok());
694 }
695
696 #[test]
697 fn valid_zstd_compression_level_try_from_i8(input in 0..=22i8) {
698 assert!(ZstdCompressionLevel::try_from(input).is_ok());
699 }
700
701 #[test]
702 fn valid_zstd_compression_level_try_from_i16(input in 0..=22i16) {
703 assert!(ZstdCompressionLevel::try_from(input).is_ok());
704 }
705
706 #[test]
707 fn valid_zstd_compression_level_try_from_i32(input in 0..=22i32) {
708 assert!(ZstdCompressionLevel::try_from(input).is_ok());
709 }
710
711 #[test]
712 fn valid_zstd_compression_level_try_from_i64(input in 0..=22i64) {
713 assert!(ZstdCompressionLevel::try_from(input).is_ok());
714 }
715
716 #[test]
717 fn valid_zstd_compression_level_try_from_u8(input in 0..=22u8) {
718 assert!(ZstdCompressionLevel::try_from(input).is_ok());
719 }
720
721 #[test]
722 fn valid_zstd_compression_level_try_from_u16(input in 0..=22u16) {
723 assert!(ZstdCompressionLevel::try_from(input).is_ok());
724 }
725
726 #[test]
727 fn valid_zstd_compression_level_try_from_u32(input in 0..=22u32) {
728 assert!(ZstdCompressionLevel::try_from(input).is_ok());
729 }
730
731 #[test]
732 fn valid_zstd_compression_level_try_from_u64(input in 0..=22u64) {
733 assert!(ZstdCompressionLevel::try_from(input).is_ok());
734 }
735 }
736
737 #[rstest]
738 #[case::too_large(Bzip2CompressionLevel::max() + 1)]
739 #[case::too_small(Bzip2CompressionLevel::min() - 1)]
740 fn create_bzip2_compression_level_fails(#[case] level: u8) -> TestResult {
741 if let Ok(level) = Bzip2CompressionLevel::new(level) {
742 return Err(format!("Should not have succeeded but created level: {level}").into());
743 }
744
745 Ok(())
746 }
747
748 #[test]
749 fn create_bzip2_compression_level_succeeds() -> TestResult {
750 if let Err(error) = Bzip2CompressionLevel::new(6) {
751 return Err(format!("Should have succeeded but raised error:\n{error}").into());
752 }
753
754 Ok(())
755 }
756
757 #[rstest]
758 #[case::too_large(GzipCompressionLevel::max() + 1)]
759 #[case::too_small(GzipCompressionLevel::min() - 1)]
760 fn create_gzip_compression_level_fails(#[case] level: u8) -> TestResult {
761 if let Ok(level) = GzipCompressionLevel::new(level) {
762 return Err(format!("Should not have succeeded but created level: {level}").into());
763 }
764
765 Ok(())
766 }
767
768 #[test]
769 fn create_gzip_compression_level_succeeds() -> TestResult {
770 if let Err(error) = GzipCompressionLevel::new(6) {
771 return Err(format!("Should have succeeded but raised error:\n{error}").into());
772 }
773
774 Ok(())
775 }
776
777 #[test]
778 fn create_xz_compression_level_fails() -> TestResult {
779 if let Ok(level) = XzCompressionLevel::new(XzCompressionLevel::max() + 1) {
780 return Err(format!("Should not have succeeded but created level: {level}").into());
781 }
782
783 Ok(())
784 }
785
786 #[test]
787 fn create_xz_compression_level_succeeds() -> TestResult {
788 if let Err(error) = XzCompressionLevel::new(6) {
789 return Err(format!("Should have succeeded but raised error:\n{error}").into());
790 }
791
792 Ok(())
793 }
794
795 #[test]
796 fn create_zstd_compression_level_fails() -> TestResult {
797 if let Ok(level) = ZstdCompressionLevel::new(ZstdCompressionLevel::max() + 1) {
798 return Err(format!("Should not have succeeded but created level: {level}").into());
799 }
800
801 Ok(())
802 }
803
804 #[test]
805 fn create_zstd_compression_level_succeeds() -> TestResult {
806 if let Err(error) = ZstdCompressionLevel::new(6) {
807 return Err(format!("Should have succeeded but raised error:\n{error}").into());
808 }
809
810 Ok(())
811 }
812
813 #[test]
815 fn default_compression_settings() -> TestResult {
816 assert!(matches!(
817 CompressionSettings::default(),
818 CompressionSettings::Zstd {
819 compression_level: _,
820 threads: _,
821 }
822 ));
823 Ok(())
824 }
825
826 #[rstest]
828 #[case::bzip2(CompressionSettings::Bzip2 { compression_level: Bzip2CompressionLevel::default()})]
829 #[case::gzip(CompressionSettings::Gzip { compression_level: GzipCompressionLevel::default()})]
830 #[case::xz(CompressionSettings::Xz { compression_level: XzCompressionLevel::default()})]
831 #[case::zstd_all_threads(CompressionSettings::Zstd { compression_level: ZstdCompressionLevel::default(), threads: ZstdThreads::new(0) })]
832 #[case::zstd_one_thread(CompressionSettings::Zstd { compression_level: ZstdCompressionLevel::default(), threads: ZstdThreads::new(1) })]
833 #[case::zstd_crazy_threads(CompressionSettings::Zstd { compression_level: ZstdCompressionLevel::default(), threads: ZstdThreads::new(99999) })]
834 fn test_compression_encoder_write(#[case] settings: CompressionSettings) -> TestResult {
835 let file = tempfile()?;
836 let mut encoder = CompressionEncoder::new(file, &settings)?;
837 let ref_encoder = encoder.by_ref();
838 let buf = &[1; 8];
839
840 let mut write_len = 0;
841 while write_len < buf.len() {
842 let len_written = ref_encoder.write(buf)?;
843 write_len += len_written;
844 }
845
846 ref_encoder.flush()?;
847
848 Ok(())
849 }
850
851 #[rstest]
854 #[case::bzip2(CompressionSettings::Bzip2 { compression_level: Bzip2CompressionLevel::default()})]
855 #[case::gzip(CompressionSettings::Gzip { compression_level: GzipCompressionLevel::default()})]
856 #[case::xz(CompressionSettings::Xz { compression_level: XzCompressionLevel::default()})]
857 #[case::zstd_all_threads(CompressionSettings::Zstd { compression_level: ZstdCompressionLevel::default(), threads: ZstdThreads::new(0) })]
858 #[case::zstd_one_thread(CompressionSettings::Zstd { compression_level: ZstdCompressionLevel::default(), threads: ZstdThreads::new(1) })]
859 #[case::zstd_crazy_threads(CompressionSettings::Zstd { compression_level: ZstdCompressionLevel::default(), threads: ZstdThreads::new(99999) })]
860 fn test_compression_encoder_write_vectored(
861 #[case] settings: CompressionSettings,
862 ) -> TestResult {
863 let file = tempfile()?;
864 let mut encoder = CompressionEncoder::new(file, &settings)?;
865 let ref_encoder = encoder.by_ref();
866
867 let data1 = [1; 8];
868 let data2 = [15; 8];
869 let io_slice1 = IoSlice::new(&data1);
870 let io_slice2 = IoSlice::new(&data2);
871
872 let mut write_len = 0;
873 while write_len < data1.len() + data2.len() {
874 let len_written = ref_encoder.write_vectored(&[io_slice1, io_slice2])?;
875 write_len += len_written;
876 }
877
878 ref_encoder.flush()?;
879
880 Ok(())
881 }
882
883 #[rstest]
885 #[case::bzip2(CompressionSettings::Bzip2 { compression_level: Bzip2CompressionLevel::default()})]
886 #[case::gzip(CompressionSettings::Gzip { compression_level: GzipCompressionLevel::default()})]
887 #[case::xz(CompressionSettings::Xz { compression_level: XzCompressionLevel::default()})]
888 #[case::zstd_all_threads(CompressionSettings::Zstd { compression_level: ZstdCompressionLevel::default(), threads: ZstdThreads::new(0) })]
889 #[case::zstd_one_thread(CompressionSettings::Zstd { compression_level: ZstdCompressionLevel::default(), threads: ZstdThreads::new(1) })]
890 #[case::zstd_crazy_threads(CompressionSettings::Zstd { compression_level: ZstdCompressionLevel::default(), threads: ZstdThreads::new(99999) })]
891 fn test_compression_encoder_write_all(#[case] settings: CompressionSettings) -> TestResult {
892 let file = tempfile()?;
893 let mut encoder = CompressionEncoder::new(file, &settings)?;
894 let ref_encoder = encoder.by_ref();
895 let buf = &[1; 8];
896
897 ref_encoder.write_all(buf)?;
898
899 ref_encoder.flush()?;
900
901 Ok(())
902 }
903
904 #[rstest]
906 #[case::bzip2(CompressionSettings::Bzip2 { compression_level: Bzip2CompressionLevel::default()})]
907 #[case::gzip(CompressionSettings::Gzip { compression_level: GzipCompressionLevel::default()})]
908 #[case::xz(CompressionSettings::Xz { compression_level: XzCompressionLevel::default()})]
909 #[case::zstd_all_threads(CompressionSettings::Zstd { compression_level: ZstdCompressionLevel::default(), threads: ZstdThreads::new(0) })]
910 #[case::zstd_one_thread(CompressionSettings::Zstd { compression_level: ZstdCompressionLevel::default(), threads: ZstdThreads::new(1) })]
911 #[case::zstd_crazy_threads(CompressionSettings::Zstd { compression_level: ZstdCompressionLevel::default(), threads: ZstdThreads::new(99999) })]
912 fn test_compression_encoder_write_fmt(#[case] settings: CompressionSettings) -> TestResult {
913 let file = tempfile()?;
914 let mut encoder = CompressionEncoder::new(file, &settings)?;
915 let ref_encoder = encoder.by_ref();
916
917 ref_encoder.write_fmt(format_args!("{:.*}", 2, 1.234567))?;
918
919 ref_encoder.flush()?;
920
921 Ok(())
922 }
923}