diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c1826f..9ebe1ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,8 +13,8 @@ jobs: toolchain: stable profile: default - run: cargo fmt --all -- --check - lint: - name: clippy lint + lint-and-test: + name: Lint with cargo clippy and test with cargo test runs-on: ubuntu-latest strategy: matrix: @@ -37,4 +37,36 @@ jobs: profile: default default: true - name: cargo clippy - run: "cargo clippy --all ${{matrix.feature}} ${{matrix.stability}}" + run: "cargo clippy --workspace ${{matrix.feature}} ${{matrix.stability}}" + - name: cargo test + run: cargo test --workspace ${{matrix.feature}} ${{matrix.stability}} + feature-deps-check: + name: Assert specific deps only exist when their corresponding features are enabled + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + default: true + - name: assert deps don't exist with --no-default-features + run: | + for DEP in libflate zstd; do + if cargo tree --no-default-features --invert $DEP; then + echo "Dependency $DEP should not exist with --no-default-features" + exit 1 + fi + done + - name: assert `deflate` feature doesn't create dep on `zstd` + run: | + if cargo tree --no-default-features --features deflate --invert zstd; then + echo "Feature `deflate` should not create dep on `zstd`" + exit 1 + fi + - name: assert `zstd` feature doesn't create dep on `libflate` + run: | + if cargo tree --no-default-features --features zstd --invert libflate; then + echo "Feature `zstd` should not create dep on `libflate`" + exit 1 + fi diff --git a/Cargo.toml b/Cargo.toml index 0d418a2..db29019 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,13 +27,11 @@ keywords = ["compression", "deflate", "macro", "include", "assets"] include = ["/src", "/LICENSE", "/README.md"] [dependencies] -include-flate-codegen = { version = "0.3.1", path = "codegen" } -include-flate-compress = { version = "0.3.1", path = "compress" } -libflate = { workspace = true } -zstd = { workspace = true } +include-flate-codegen = { version = "0.3.1", path = "codegen", default-features = false } +include-flate-compress = { version = "0.3.1", path = "compress", default-features = false } [features] default = ["deflate", "zstd"] -deflate = ["include-flate-compress/deflate"] -zstd = ["include-flate-compress/zstd"] +deflate = ["include-flate-codegen/deflate", "include-flate-compress/deflate"] +zstd = ["include-flate-codegen/zstd", "include-flate-compress/zstd"] no-compression-warnings = ["include-flate-codegen/no-compression-warnings"] diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 63d7a35..8edc160 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -12,14 +12,14 @@ description = "Macro codegen for the include-flate crate" proc-macro = true [dependencies] -libflate = { workspace = true } proc-macro2 = "1.0.95" quote = "1.0.40" syn = { version = "2.0.104", features = ["full"] } -zstd = { workspace = true } -include-flate-compress = { version = "0.3.1", path = "../compress" } +include-flate-compress = { version = "0.3.1", path = "../compress", default-features = false } proc-macro-error = "1.0.4" [features] -default = [] +default = ["deflate", "zstd"] +deflate = ["include-flate-compress/deflate"] +zstd = ["include-flate-compress/zstd"] no-compression-warnings = [] diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 7caea68..109d796 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -87,11 +87,24 @@ impl syn::parse::Parse for FlateArgs { } else { let lookahead = input.lookahead1(); if lookahead.peek(kw::deflate) { - input.parse::()?; - Some(CompressionMethodTy(CompressionMethod::Deflate)) + #[cfg(feature = "deflate")] + { + input.parse::()?; + Some(CompressionMethodTy(CompressionMethod::Deflate)) + } + #[cfg(not(feature = "deflate"))] + return Err(Error::new( + input.span(), + "Please enable the `deflate` feature", + )); } else if lookahead.peek(kw::zstd) { - input.parse::()?; - Some(CompressionMethodTy(CompressionMethod::Zstd)) + #[cfg(feature = "zstd")] + { + input.parse::()?; + Some(CompressionMethodTy(CompressionMethod::Zstd)) + } + #[cfg(not(feature = "zstd"))] + return Err(Error::new(input.span(), "Please enable the `zstd` feature")); } else { return Err(lookahead.error()); } @@ -106,7 +119,7 @@ mod kw { syn::custom_keyword!(zstd); } -#[derive(Debug)] +#[derive(Debug, Default)] struct CompressionMethodTy(CompressionMethod); fn compression_ratio(original_size: u64, compressed_size: u64) -> f64 { @@ -122,9 +135,7 @@ fn inner(ts: TokenStream, utf8: bool) -> syn::Result> { let args: FlateArgs = syn::parse2::(ts.to_owned().into())?; let path = PathBuf::from_str(&args.path.value()).map_err(emap)?; - let algo = args - .algorithm - .unwrap_or(CompressionMethodTy(CompressionMethod::Deflate)); + let algo = args.algorithm.unwrap_or_default(); if path.is_absolute() { Err(emap("absolute paths are not supported"))?; diff --git a/compress/src/lib.rs b/compress/src/lib.rs index 2c509e9..d332529 100644 --- a/compress/src/lib.rs +++ b/compress/src/lib.rs @@ -16,9 +16,11 @@ #[cfg(not(any(feature = "zstd", feature = "deflate")))] compile_error!("You must enable either the `deflate` or `zstd` feature."); +#[cfg(feature = "zstd")] +use std::io::BufReader; use std::{ fmt, - io::{self, BufRead, BufReader, Read, Seek, Write}, + io::{self, BufRead, Read, Seek, Write}, }; #[cfg(feature = "deflate")] diff --git a/fail_tests/009f-str.rs b/fail_tests/009f-str.rs index 72bfd9b..05dd77a 100644 --- a/fail_tests/009f-str.rs +++ b/fail_tests/009f-str.rs @@ -18,7 +18,9 @@ include!("../test_util.rs"); use include_flate::flate; flate!(pub static DATA: str from "assets/009f.dat"); +#[cfg(feature = "deflate")] flate!(pub static DATA: str from "assets/009f.dat" with deflate); +#[cfg(feature = "zstd")] flate!(pub static DATA: str from "assets/009f.dat" with zstd); #[test] diff --git a/src/lib.rs b/src/lib.rs index cecb840..4b63b86 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -99,7 +99,8 @@ macro_rules! flate { $(#[$meta])* $(pub $(($($vis)+))?)? static $name: ::std::sync::LazyLock<::std::vec::Vec> = ::std::sync::LazyLock::new(|| { - $crate::decode($crate::codegen::deflate_file!($path), None) + let algo = $crate::__parse_algo!($($algo)?); + $crate::decode($crate::codegen::deflate_file!($path $($algo)?), Some(algo)) }); }; ($(#[$meta:meta])* @@ -109,12 +110,8 @@ macro_rules! flate { $(#[$meta])* $(pub $(($($vis)+))?)? static $name: ::std::sync::LazyLock<::std::string::String> = ::std::sync::LazyLock::new(|| { - let algo = match stringify!($($algo)?){ - "deflate" => $crate::CompressionMethod::Deflate, - "zstd" => $crate::CompressionMethod::Zstd, - _ => $crate::CompressionMethod::default(), - }; - $crate::decode_string($crate::codegen::deflate_utf8_file!($path $($algo)?), Some($crate::CompressionMethodTy(algo))) + let algo = $crate::__parse_algo!($($algo)?); + $crate::decode_string($crate::codegen::deflate_utf8_file!($path $($algo)?), Some(algo)) }); }; ($(#[$meta:meta])* @@ -124,17 +121,31 @@ macro_rules! flate { $(#[$meta])* $(pub $(($($vis)+))?)? static $name: ::std::sync::LazyLock<$crate::IFlate> = ::std::sync::LazyLock::new(|| { - let algo = match stringify!($($algo)?){ - "deflate" => $crate::CompressionMethod::Deflate, - "zstd" => $crate::CompressionMethod::Zstd, - _ => $crate::CompressionMethod::default(), - }; + let algo = $crate::__parse_algo!($($algo)?); let compressed = $crate::codegen::deflate_file!($path $($algo)?); $crate::IFlate::new(compressed, algo) }); }; } +/// Helper macro to parse the user-specified algorithm. +#[doc(hidden)] +#[macro_export] +macro_rules! __parse_algo { + () => { + $crate::CompressionMethod::default() + }; + (deflate) => { + $crate::CompressionMethod::Deflate + }; + (zstd) => { + $crate::CompressionMethod::Zstd + }; + ($other:ident) => { + compile_error!("Unknown compression algorithm: {}", stringify!($other)) + }; +} + #[derive(Debug)] pub struct IFlate { compressed: &'static [u8], @@ -152,7 +163,7 @@ impl IFlate { } pub fn decoded(&self) -> Vec { - decode(&self.compressed, Some(CompressionMethodTy(self.algo))) + decode(&self.compressed, Some(self.algo)) } pub fn decode_string(&self) -> Result { @@ -164,23 +175,12 @@ impl IFlate { } } -#[derive(Debug)] -pub struct CompressionMethodTy(pub CompressionMethod); - -impl Into for CompressionMethodTy { - fn into(self) -> CompressionMethod { - self.0 - } -} - #[doc(hidden)] #[allow(private_interfaces)] -pub fn decode(bytes: &[u8], algo: Option) -> Vec { +pub fn decode(bytes: &[u8], algo: Option) -> Vec { use std::io::Cursor; - let algo: CompressionMethod = algo - .unwrap_or(CompressionMethodTy(CompressionMethod::Deflate)) - .into(); + let algo: CompressionMethod = algo.unwrap_or_default().into(); let mut source = Cursor::new(bytes); let mut ret = Vec::new(); @@ -194,7 +194,7 @@ pub fn decode(bytes: &[u8], algo: Option) -> Vec { #[doc(hidden)] #[allow(private_interfaces)] -pub fn decode_string(bytes: &[u8], algo: Option) -> String { +pub fn decode_string(bytes: &[u8], algo: Option) -> String { // We should have checked for utf8 correctness in encode_utf8_file! String::from_utf8(decode(bytes, algo)) .expect("flate_str has malformed UTF-8 despite checked at compile time") diff --git a/test_util.rs b/test_util.rs index 7a80159..eb42d06 100644 --- a/test_util.rs +++ b/test_util.rs @@ -18,7 +18,7 @@ use std::io::{Read, Seek, SeekFrom}; use std::path::{Path, PathBuf}; use std::str::from_utf8; -use include_flate_compress::{apply_compression, apply_decompression, CompressionMethod}; +use include_flate_compress::{CompressionMethod, apply_compression, apply_decompression}; pub fn get_file_path>(relative_from: Option<&Path>, path: P) -> PathBuf { let cargo_manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); @@ -58,7 +58,9 @@ pub fn verify_compression>(name: P, data: &[u8], method: Compress } pub fn verify>(name: P, data: &[u8]) { + #[cfg(feature = "deflate")] verify_compression(&name, data, CompressionMethod::Deflate); + #[cfg(feature = "zstd")] verify_compression(&name, data, CompressionMethod::Zstd); assert_eq!(read_file(&name), data); } @@ -71,7 +73,11 @@ pub fn verify_str(name: &str, data: &str) { ); } -pub fn verify_iflate>(name: P, method: CompressionMethod, iflate: &include_flate::IFlate) { +pub fn verify_iflate>( + name: P, + method: CompressionMethod, + iflate: &include_flate::IFlate, +) { assert_eq!(iflate.algo(), method); let path = get_file_path(None, &name); let mut file = File::open(&path).unwrap(); diff --git a/tests/009f.rs b/tests/009f.rs index 4042f61..72cc022 100644 --- a/tests/009f.rs +++ b/tests/009f.rs @@ -18,16 +18,24 @@ include!("../test_util.rs"); use include_flate::flate; flate!(pub static DATA1: [u8] from "assets/009f.dat"); +#[cfg(feature = "deflate")] flate!(pub static DATA2: [u8] from "assets/009f.dat" with deflate); +#[cfg(feature = "zstd")] flate!(pub static DATA3: [u8] from "assets/009f.dat" with zstd); +#[cfg(feature = "deflate")] flate!(pub static DATA4: IFlate from "assets/009f.dat" with deflate); +#[cfg(feature = "zstd")] flate!(pub static DATA5: IFlate from "assets/009f.dat" with zstd); #[test] fn test() { verify("009f.dat", &DATA1); + #[cfg(feature = "deflate")] verify("009f.dat", &DATA2); + #[cfg(feature = "zstd")] verify("009f.dat", &DATA3); + #[cfg(feature = "deflate")] verify_iflate("009f.dat", CompressionMethod::Deflate, &DATA4); + #[cfg(feature = "zstd")] verify_iflate("009f.dat", CompressionMethod::Zstd, &DATA5); } diff --git a/tests/ascii-control.rs b/tests/ascii-control.rs index da0d7fe..bbf9b0e 100644 --- a/tests/ascii-control.rs +++ b/tests/ascii-control.rs @@ -18,16 +18,24 @@ include!("../test_util.rs"); use include_flate::flate; flate!(pub static DATA1: str from "assets/ascii-control.txt"); +#[cfg(feature = "deflate")] flate!(pub static DATA2: str from "assets/ascii-control.txt" with deflate); +#[cfg(feature = "zstd")] flate!(pub static DATA3: str from "assets/ascii-control.txt" with zstd); +#[cfg(feature = "deflate")] flate!(pub static DATA4: IFlate from "assets/ascii-control.txt" with deflate); +#[cfg(feature = "zstd")] flate!(pub static DATA5: IFlate from "assets/ascii-control.txt" with zstd); #[test] fn test() { verify_str("ascii-control.txt", &DATA1); + #[cfg(feature = "deflate")] verify_str("ascii-control.txt", &DATA2); + #[cfg(feature = "zstd")] verify_str("ascii-control.txt", &DATA3); + #[cfg(feature = "deflate")] verify_iflate("ascii-control.txt", CompressionMethod::Deflate, &DATA4); + #[cfg(feature = "zstd")] verify_iflate("ascii-control.txt", CompressionMethod::Zstd, &DATA5); } diff --git a/tests/ascii-printable.rs b/tests/ascii-printable.rs index 3513517..db46312 100644 --- a/tests/ascii-printable.rs +++ b/tests/ascii-printable.rs @@ -18,16 +18,24 @@ include!("../test_util.rs"); use include_flate::flate; flate!(pub static DATA1: str from "assets/ascii-printable.txt"); +#[cfg(feature = "deflate")] flate!(pub static DATA2: str from "assets/ascii-printable.txt" with deflate); +#[cfg(feature = "zstd")] flate!(pub static DATA3: str from "assets/ascii-printable.txt" with zstd); +#[cfg(feature = "deflate")] flate!(pub static DATA4: IFlate from "assets/ascii-printable.txt" with deflate); +#[cfg(feature = "zstd")] flate!(pub static DATA5: IFlate from "assets/ascii-printable.txt" with zstd); #[test] fn test() { verify_str("ascii-printable.txt", &DATA1); + #[cfg(feature = "deflate")] verify_str("ascii-printable.txt", &DATA2); + #[cfg(feature = "zstd")] verify_str("ascii-printable.txt", &DATA3); + #[cfg(feature = "deflate")] verify_iflate("ascii-printable.txt", CompressionMethod::Deflate, &DATA4); + #[cfg(feature = "zstd")] verify_iflate("ascii-printable.txt", CompressionMethod::Zstd, &DATA5); } diff --git a/tests/base64.rs b/tests/base64.rs index 31605c6..5171c3f 100644 --- a/tests/base64.rs +++ b/tests/base64.rs @@ -21,16 +21,24 @@ pub static DATA_RAW: &[u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/base64.txt")); flate!(pub static DATA1: str from "assets/base64.txt"); +#[cfg(feature = "deflate")] flate!(pub static DATA2: str from "assets/base64.txt" with deflate); +#[cfg(feature = "zstd")] flate!(pub static DATA3: str from "assets/base64.txt" with zstd); +#[cfg(feature = "deflate")] flate!(pub static DATA4: IFlate from "assets/base64.txt" with deflate); +#[cfg(feature = "zstd")] flate!(pub static DATA5: IFlate from "assets/base64.txt" with zstd); #[test] fn test() { verify_str("base64.txt", &DATA1); + #[cfg(feature = "deflate")] verify_str("base64.txt", &DATA2); + #[cfg(feature = "zstd")] verify_str("base64.txt", &DATA3); + #[cfg(feature = "deflate")] verify_iflate("base64.txt", CompressionMethod::Deflate, &DATA4); + #[cfg(feature = "zstd")] verify_iflate("base64.txt", CompressionMethod::Zstd, &DATA5); } diff --git a/tests/chinese.rs b/tests/chinese.rs index ef60bc8..a092f05 100644 --- a/tests/chinese.rs +++ b/tests/chinese.rs @@ -18,16 +18,24 @@ include!("../test_util.rs"); use include_flate::flate; flate!(pub static DATA1: str from "assets/chinese.txt"); +#[cfg(feature = "deflate")] flate!(pub static DATA2: str from "assets/chinese.txt" with deflate); +#[cfg(feature = "zstd")] flate!(pub static DATA3: str from "assets/chinese.txt" with zstd); +#[cfg(feature = "deflate")] flate!(pub static DATA4: IFlate from "assets/chinese.txt" with deflate); +#[cfg(feature = "zstd")] flate!(pub static DATA5: IFlate from "assets/chinese.txt" with zstd); #[test] fn test() { verify_str("chinese.txt", &DATA1); + #[cfg(feature = "deflate")] verify_str("chinese.txt", &DATA2); + #[cfg(feature = "zstd")] verify_str("chinese.txt", &DATA3); + #[cfg(feature = "deflate")] verify_iflate("chinese.txt", CompressionMethod::Deflate, &DATA4); + #[cfg(feature = "zstd")] verify_iflate("chinese.txt", CompressionMethod::Zstd, &DATA5); } diff --git a/tests/emoji.rs b/tests/emoji.rs index 9c66d3c..24efc71 100644 --- a/tests/emoji.rs +++ b/tests/emoji.rs @@ -18,16 +18,24 @@ include!("../test_util.rs"); use include_flate::flate; flate!(pub static DATA1: str from "assets/emoji.txt"); +#[cfg(feature = "deflate")] flate!(pub static DATA2: str from "assets/emoji.txt" with deflate); +#[cfg(feature = "zstd")] flate!(pub static DATA3: str from "assets/emoji.txt" with zstd); +#[cfg(feature = "deflate")] flate!(pub static DATA4: IFlate from "assets/emoji.txt" with deflate); +#[cfg(feature = "zstd")] flate!(pub static DATA5: IFlate from "assets/emoji.txt" with zstd); #[test] fn test() { verify_str("emoji.txt", &DATA1); + #[cfg(feature = "deflate")] verify_str("emoji.txt", &DATA2); + #[cfg(feature = "zstd")] verify_str("emoji.txt", &DATA3); + #[cfg(feature = "deflate")] verify_iflate("emoji.txt", CompressionMethod::Deflate, &DATA4); + #[cfg(feature = "zstd")] verify_iflate("emoji.txt", CompressionMethod::Zstd, &DATA5); } diff --git a/tests/ff.rs b/tests/ff.rs index bef264e..8bc13e1 100644 --- a/tests/ff.rs +++ b/tests/ff.rs @@ -18,16 +18,24 @@ include!("../test_util.rs"); use include_flate::flate; flate!(pub static DATA1: [u8] from "assets/ff.dat"); +#[cfg(feature = "deflate")] flate!(pub static DATA2: [u8] from "assets/ff.dat" with deflate); +#[cfg(feature = "zstd")] flate!(pub static DATA3: [u8] from "assets/ff.dat" with zstd); +#[cfg(feature = "deflate")] flate!(pub static DATA4: IFlate from "assets/ff.dat" with deflate); +#[cfg(feature = "zstd")] flate!(pub static DATA5: IFlate from "assets/ff.dat" with zstd); #[test] fn test() { verify("ff.dat", &DATA1); + #[cfg(feature = "deflate")] verify("ff.dat", &DATA2); + #[cfg(feature = "zstd")] verify("ff.dat", &DATA3); + #[cfg(feature = "deflate")] verify_iflate("ff.dat", CompressionMethod::Deflate, &DATA4); + #[cfg(feature = "zstd")] verify_iflate("ff.dat", CompressionMethod::Zstd, &DATA5); } diff --git a/tests/random.rs b/tests/random.rs index 956d434..e1254e0 100644 --- a/tests/random.rs +++ b/tests/random.rs @@ -18,16 +18,24 @@ include!("../test_util.rs"); use include_flate::flate; flate!(pub static DATA1: [u8] from "assets/random.dat"); +#[cfg(feature = "deflate")] flate!(pub static DATA2: [u8] from "assets/random.dat" with deflate); +#[cfg(feature = "zstd")] flate!(pub static DATA3: [u8] from "assets/random.dat" with zstd); +#[cfg(feature = "deflate")] flate!(pub static DATA4: IFlate from "assets/random.dat" with deflate); +#[cfg(feature = "zstd")] flate!(pub static DATA5: IFlate from "assets/random.dat" with zstd); #[test] fn test() { verify("random.dat", &DATA1); + #[cfg(feature = "deflate")] verify("random.dat", &DATA2); + #[cfg(feature = "zstd")] verify("random.dat", &DATA3); + #[cfg(feature = "deflate")] verify_iflate("random.dat", CompressionMethod::Deflate, &DATA4); + #[cfg(feature = "zstd")] verify_iflate("random.dat", CompressionMethod::Zstd, &DATA5); } diff --git a/tests/zero.rs b/tests/zero.rs index eb3bde7..add2ea9 100644 --- a/tests/zero.rs +++ b/tests/zero.rs @@ -18,16 +18,24 @@ include!("../test_util.rs"); use include_flate::flate; flate!(pub static DATA1: [u8] from "assets/zero.dat"); +#[cfg(feature = "deflate")] flate!(pub static DATA2: [u8] from "assets/zero.dat" with deflate); +#[cfg(feature = "zstd")] flate!(pub static DATA3: [u8] from "assets/zero.dat" with zstd); +#[cfg(feature = "deflate")] flate!(pub static DATA4: IFlate from "assets/zero.dat" with deflate); +#[cfg(feature = "zstd")] flate!(pub static DATA5: IFlate from "assets/zero.dat" with zstd); #[test] fn test() { verify("zero.dat", &DATA1); + #[cfg(feature = "deflate")] verify("zero.dat", &DATA2); + #[cfg(feature = "zstd")] verify("zero.dat", &DATA3); + #[cfg(feature = "deflate")] verify_iflate("zero.dat", CompressionMethod::Deflate, &DATA4); + #[cfg(feature = "zstd")] verify_iflate("zero.dat", CompressionMethod::Zstd, &DATA5); }