Skip to content

Commit b16f33d

Browse files
add cfg_if! and cfg_if_expr! macro_rules macros
1 parent 72cce40 commit b16f33d

File tree

8 files changed

+249
-104
lines changed

8 files changed

+249
-104
lines changed

examples/write_dir.rs

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use anyhow::Context;
44
use clap::{Parser, ValueEnum};
55
use std::io::prelude::*;
6-
use zip::{result::ZipError, write::SimpleFileOptions};
6+
use zip::{cfg_if_expr, result::ZipError, write::SimpleFileOptions};
77

88
use std::fs::File;
99
use std::path::{Path, PathBuf};
@@ -40,42 +40,34 @@ fn real_main() -> i32 {
4040
let dst_file = &args.destination;
4141
let method = match args.compression_method {
4242
CompressionMethod::Stored => zip::CompressionMethod::Stored,
43-
CompressionMethod::Deflated => {
44-
#[cfg(not(feature = "deflate-flate2"))]
45-
{
43+
CompressionMethod::Deflated => cfg_if_expr! {
44+
#[cfg(feature = "deflate-flate2")] => zip::CompressionMethod::Deflated,
45+
_ => {
4646
println!("The `deflate-flate2` feature is not enabled");
47-
return 1;
47+
std::process::exit(1)
4848
}
49-
#[cfg(feature = "deflate-flate2")]
50-
zip::CompressionMethod::Deflated
51-
}
52-
CompressionMethod::Bzip2 => {
53-
#[cfg(not(feature = "bzip2"))]
54-
{
49+
},
50+
CompressionMethod::Bzip2 => cfg_if_expr! {
51+
#[cfg(feature = "bzip2")] => zip::CompressionMethod::Bzip2,
52+
_ => {
5553
println!("The `bzip2` feature is not enabled");
56-
return 1;
54+
std::process::exit(1);
5755
}
58-
#[cfg(feature = "bzip2")]
59-
zip::CompressionMethod::Bzip2
60-
}
61-
CompressionMethod::Xz => {
62-
#[cfg(not(feature = "xz"))]
63-
{
56+
},
57+
CompressionMethod::Xz => cfg_if_expr! {
58+
#[cfg(feature = "xz")] => zip::CompressionMethod::Xz,
59+
_ => {
6460
println!("The `xz` feature is not enabled");
65-
return 1;
61+
std::process::exit(1);
6662
}
67-
#[cfg(feature = "xz")]
68-
zip::CompressionMethod::Xz
69-
}
70-
CompressionMethod::Zstd => {
71-
#[cfg(not(feature = "zstd"))]
72-
{
63+
},
64+
CompressionMethod::Zstd => cfg_if_expr! {
65+
#[cfg(feature = "zstd")] => zip::CompressionMethod::Zstd,
66+
_ => {
7367
println!("The `zstd` feature is not enabled");
74-
return 1;
68+
std::process::exit(1);
7569
}
76-
#[cfg(feature = "zstd")]
77-
zip::CompressionMethod::Zstd
78-
}
70+
},
7971
};
8072
match doit(src_dir, dst_file, method) {
8173
Ok(_) => println!("done: {src_dir:?} written to {dst_file:?}"),

fuzz_read/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use afl::fuzz;
22
use std::io::{Read, Seek, SeekFrom};
33
use tikv_jemallocator::Jemalloc;
4+
use zip::cfg_if;
45
use zip::read::read_zipfile_from_stream;
56

67
const MAX_BYTES_TO_READ: u64 = 1 << 24;

src/compression.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
33
use std::{fmt, io};
44

5+
use crate::cfg_if_expr;
6+
57
#[allow(deprecated)]
68
/// Identifies the storage format used to compress a file within a ZIP archive.
79
///
@@ -228,11 +230,10 @@ impl CompressionMethod {
228230

229231
impl Default for CompressionMethod {
230232
fn default() -> Self {
231-
#[cfg(feature = "_deflate-any")]
232-
return CompressionMethod::Deflated;
233-
234-
#[cfg(not(feature = "_deflate-any"))]
235-
return CompressionMethod::Stored;
233+
cfg_if_expr! {
234+
#[cfg(feature = "_deflate-any")] => CompressionMethod::Deflated,
235+
_ => CompressionMethod::Stored
236+
}
236237
}
237238
}
238239

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,6 @@ zip = \"="]
7171
#[doc = "\"\n\
7272
```"]
7373
pub mod unstable;
74+
75+
#[doc(hidden)]
76+
pub mod macros;

src/macros.rs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
//! Macros used internally.
2+
//!
3+
//! These may technically be exported, but that's only to make them available to internal
4+
//! project dependencies. The `#[doc(hidden)]` mark indicates that these are not stable or supported
5+
//! APIs, and should not be relied upon by external dependees.
6+
7+
/// The single macro export of the [`cfg-if`](https://docs.rs/cfg-if) crate.
8+
///
9+
/// It is packaged here to avoid pulling in another dependency. The stdlib does the same[^1].
10+
///
11+
/// [^1]: https://github.com/rust-lang/rust/blob/a2db9280539229a3b8a084a09886670a57bc7e9c/library/compiler-builtins/libm/src/math/support/macros.rs#L1
12+
#[doc(hidden)]
13+
#[macro_export]
14+
macro_rules! cfg_if {
15+
// match if/else chains with a final `else`
16+
(
17+
$(
18+
if #[cfg( $i_meta:meta )] { $( $i_tokens:tt )* }
19+
) else+
20+
else { $( $e_tokens:tt )* }
21+
) => {
22+
$crate::cfg_if! {
23+
@__items () ;
24+
$(
25+
(( $i_meta ) ( $( $i_tokens )* )) ,
26+
)+
27+
(() ( $( $e_tokens )* )) ,
28+
};
29+
};
30+
31+
// match if/else chains lacking a final `else`
32+
(
33+
if #[cfg( $i_meta:meta )] { $( $i_tokens:tt )* }
34+
$(
35+
else if #[cfg( $e_meta:meta )] { $( $e_tokens:tt )* }
36+
)*
37+
) => {
38+
$crate::cfg_if! {
39+
@__items () ;
40+
(( $i_meta ) ( $( $i_tokens )* )) ,
41+
$(
42+
(( $e_meta ) ( $( $e_tokens )* )) ,
43+
)*
44+
};
45+
};
46+
47+
// Internal and recursive macro to emit all the items
48+
//
49+
// Collects all the previous cfgs in a list at the beginning, so they can be
50+
// negated. After the semicolon are all the remaining items.
51+
(@__items ( $( $_:meta , )* ) ; ) => {};
52+
(
53+
@__items ( $( $no:meta , )* ) ;
54+
(( $( $yes:meta )? ) ( $( $tokens:tt )* )) ,
55+
$( $rest:tt , )*
56+
) => {
57+
// Emit all items within one block, applying an appropriate #[cfg]. The
58+
// #[cfg] will require all `$yes` matchers specified and must also negate
59+
// all previous matchers.
60+
#[cfg(all(
61+
$( $yes , )?
62+
not(any( $( $no ),* ))
63+
))]
64+
$crate::cfg_if! { @__identity $( $tokens )* }
65+
66+
// Recurse to emit all other items in `$rest`, and when we do so add all
67+
// our `$yes` matchers to the list of `$no` matchers as future emissions
68+
// will have to negate everything we just matched as well.
69+
$crate::cfg_if! {
70+
@__items ( $( $no , )* $( $yes , )? ) ;
71+
$( $rest , )*
72+
};
73+
};
74+
75+
// Internal macro to make __apply work out right for different match types,
76+
// because of how macros match/expand stuff.
77+
(@__identity $( $tokens:tt )* ) => {
78+
$( $tokens )*
79+
};
80+
}
81+
82+
/// Similar to [`cfg_if`](cfg_if), but accepts a list of expressions, and generates an internal
83+
/// closure to return each value.
84+
///
85+
/// The generated closure is non-[`const`](const@keyword), so cannot be used inside `const` methods.
86+
#[doc(hidden)]
87+
#[macro_export]
88+
macro_rules! cfg_if_expr {
89+
// Match :, chains, maybe with a final _: catchall clause.
90+
(
91+
$(
92+
#[cfg( $i_meta:meta )] => $i_val:expr
93+
),+ ,
94+
_ => $rem_val:expr $(,)?
95+
) => {
96+
(|| {
97+
$crate::cfg_if_expr! {
98+
@__items ();
99+
$(
100+
(( $i_meta ) ( return $i_val ; )) ,
101+
)+
102+
(() ( return $rem_val ; )) ,
103+
}
104+
})()
105+
};
106+
(
107+
$(
108+
#[cfg( $i_meta:meta )] => $i_val:expr
109+
),+ $(,)?
110+
) => {
111+
(|| {
112+
$crate::cfg_if_expr! {
113+
@__items ();
114+
$(
115+
(( $i_meta ) ( return $i_val ; )) ,
116+
)+
117+
}
118+
})()
119+
};
120+
121+
(@__items ( $( $_:meta , )* ) ; ) => {};
122+
(
123+
@__items ( $( $no:meta , )* ) ;
124+
(( $( $yes:meta )? ) ( $( $tokens:tt )* )) ,
125+
$( $rest:tt , )*
126+
) => {
127+
#[cfg(all(
128+
$( $yes , )?
129+
not(any( $( $no ),* ))
130+
))]
131+
$crate::cfg_if_expr! { @__identity $( $tokens )* }
132+
133+
$crate::cfg_if_expr! {
134+
@__items ( $( $no , )* $( $yes , )? ) ;
135+
$( $rest , )*
136+
};
137+
};
138+
(@__identity $( $tokens:tt )* ) => {
139+
$( $tokens )*
140+
};
141+
}

src/read.rs

Lines changed: 55 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
#[cfg(feature = "aes-crypto")]
44
use crate::aes::{AesReader, AesReaderValid};
5+
use crate::cfg_if;
56
use crate::compression::{CompressionMethod, Decompressor};
67
use crate::cp437::FromCp437;
78
use crate::crc32::Crc32Reader;
@@ -172,16 +173,20 @@ impl<'a, R: Read> CryptoReader<'a, R> {
172173

173174
/// Returns `true` if the data is encrypted using AE2.
174175
pub const fn is_ae2_encrypted(&self) -> bool {
175-
#[cfg(feature = "aes-crypto")]
176-
return matches!(
177-
self,
178-
CryptoReader::Aes {
179-
vendor_version: AesVendorVersion::Ae2,
180-
..
176+
cfg_if! {
177+
if #[cfg(feature = "aes-crypto")] {
178+
#[allow(clippy::needless_return)]
179+
return matches!(
180+
self,
181+
CryptoReader::Aes {
182+
vendor_version: AesVendorVersion::Ae2,
183+
..
184+
}
185+
);
186+
} else {
187+
return false;
181188
}
182-
);
183-
#[cfg(not(feature = "aes-crypto"))]
184-
false
189+
}
185190
}
186191
}
187192

@@ -454,34 +459,32 @@ pub(crate) fn make_symlink<T>(
454459
return Err(invalid!("Invalid UTF-8 as symlink target"));
455460
};
456461

457-
#[cfg(not(any(unix, windows)))]
458-
{
459-
use std::fs::File;
460-
let output = File::create(outpath);
461-
output?.write_all(target)?;
462-
}
463-
#[cfg(unix)]
464-
{
465-
std::os::unix::fs::symlink(Path::new(&target_str), outpath)?;
466-
}
467-
#[cfg(windows)]
468-
{
469-
let target = Path::new(OsStr::new(&target_str));
470-
let target_is_dir_from_archive =
471-
existing_files.contains_key(target_str) && is_dir(target_str);
472-
let target_is_dir = if target_is_dir_from_archive {
473-
true
474-
} else if let Ok(meta) = std::fs::metadata(target) {
475-
meta.is_dir()
476-
} else {
477-
false
478-
};
479-
if target_is_dir {
480-
std::os::windows::fs::symlink_dir(target, outpath)?;
462+
cfg_if! {
463+
if #[cfg(unix)] {
464+
std::os::unix::fs::symlink(Path::new(&target_str), outpath)?;
465+
} else if #[cfg(windows)] {
466+
let target = Path::new(OsStr::new(&target_str));
467+
let target_is_dir_from_archive =
468+
existing_files.contains_key(target_str) && is_dir(target_str);
469+
let target_is_dir = if target_is_dir_from_archive {
470+
true
471+
} else if let Ok(meta) = std::fs::metadata(target) {
472+
meta.is_dir()
473+
} else {
474+
false
475+
};
476+
if target_is_dir {
477+
std::os::windows::fs::symlink_dir(target, outpath)?;
478+
} else {
479+
std::os::windows::fs::symlink_file(target, outpath)?;
480+
}
481481
} else {
482-
std::os::windows::fs::symlink_file(target, outpath)?;
482+
use std::fs::File;
483+
let output = File::create(outpath);
484+
output?.write_all(target)?;
483485
}
484486
}
487+
485488
Ok(())
486489
}
487490

@@ -890,25 +893,30 @@ impl<R: Read + Seek> ZipArchive<R> {
890893
let mut outpath = directory.clone();
891894
file.safe_prepare_path(directory.as_ref(), &mut outpath, root_dir.as_ref())?;
892895

893-
let symlink_target = if file.is_symlink() && (cfg!(unix) || cfg!(windows)) {
894-
let mut target = Vec::with_capacity(file.size() as usize);
895-
file.read_to_end(&mut target)?;
896-
Some(target)
897-
} else {
898-
if file.is_dir() {
899-
crate::read::make_writable_dir_all(&outpath)?;
900-
continue;
896+
let mut symlink_target: Option<Vec<u8>> = None;
897+
cfg_if! {
898+
if #[cfg(any(unix, windows))] {
899+
if file.is_symlink() {
900+
let mut target = Vec::with_capacity(file.size() as usize);
901+
file.read_to_end(&mut target)?;
902+
symlink_target = Some(target);
903+
}
904+
} else {
905+
if file.is_symlink() {
906+
todo!("no support for symlinks outside of unix or windows platforms");
907+
}
901908
}
902-
None
903-
};
904-
905-
drop(file);
909+
}
910+
if symlink_target.is_none() && file.is_dir() {
911+
crate::read::make_writable_dir_all(&outpath)?;
912+
continue;
913+
}
906914

907915
if let Some(target) = symlink_target {
916+
drop(file);
908917
make_symlink(&outpath, &target, &self.shared.files)?;
909918
continue;
910919
}
911-
let mut file = self.by_index(i)?;
912920
let mut outfile = fs::File::create(&outpath)?;
913921

914922
io::copy(&mut file, &mut outfile)?;

0 commit comments

Comments
 (0)