diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index adf73fb..dc4d72c 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -2,9 +2,9 @@ name: Rust on: push: - branches: [ master ] + branches: [master] pull_request: - branches: [ master ] + branches: [master] jobs: build-unix: runs-on: ${{ matrix.os }}-latest @@ -13,10 +13,10 @@ jobs: channel: [stable, beta, nightly] os: [ubuntu, macos] steps: - - uses: actions/checkout@v2 - - run: rustup default ${{ matrix.channel }} - - run: cargo build --verbose --all-targets - - run: cargo test + - uses: actions/checkout@v2 + - run: rustup default ${{ matrix.channel }} + - run: cargo build --verbose --all-targets + - run: cargo test build-windows: runs-on: windows-latest strategy: @@ -31,13 +31,16 @@ jobs: - arch: i686 variant: gnu steps: - - uses: actions/checkout@v2 - - run: choco install msys2 - if: matrix.variant == 'gnu' - - run: rustup default ${{ matrix.channel }} - - run: rustup target add ${{ matrix.arch }}-pc-windows-${{ matrix.variant }} - - name: Build - run: cargo build --verbose --target ${{ matrix.arch }}-pc-windows-${{ matrix.variant }} - - name: Run tests - if: matrix.arch != 'aarch64' - run: cargo test --verbose --target ${{ matrix.arch }}-pc-windows-${{ matrix.variant }} + - uses: actions/checkout@v2 + - run: choco install msys2 + if: matrix.variant == 'gnu' + - run: rustup default ${{ matrix.channel }} + - run: rustup target add ${{ matrix.arch }}-pc-windows-${{ matrix.variant }} + - name: Build + run: cargo build --verbose --target ${{ matrix.arch }}-pc-windows-${{ matrix.variant }} + - name: Run tests (nightly) + if: (matrix.arch != 'aarch64') && (matrix.channel == 'nightly') + run: cargo test --verbose --target ${{ matrix.arch }}-pc-windows-${{ matrix.variant }} --features nightly + - name: Run tests + if: (matrix.arch != 'aarch64') && (matrix.channel != 'nightly') + run: cargo test --verbose --target ${{ matrix.arch }}-pc-windows-${{ matrix.variant }} diff --git a/Cargo.toml b/Cargo.toml index 3e11e09..d8dd7f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,8 @@ [package] -authors = ["Erin P. ", "Robert C. "] +authors = [ + "Erin P. ", + "Robert C. ", +] categories = ["filesystem"] description = "A safe, reliable implementation of remove_dir_all for Windows" edition = "2018" @@ -17,11 +20,24 @@ readme = "README.md" repository = "https://github.com/XAMPPRocky/remove_dir_all.git" version = "0.7.1-alpha.0" +[features] +default = [] +nightly = [] + +[dependencies] +cfg-if = "1.0.0" + [target.'cfg(windows)'.dependencies] log = "0.4.11" num_cpus = "1.13" rayon = "1.4" -winapi = {version = "0.3", features = ["std", "errhandlingapi", "winerror", "fileapi", "winbase"]} +winapi = { version = "0.3", features = [ + "std", + "errhandlingapi", + "winerror", + "fileapi", + "winbase", +] } [target.'cfg(not(windows))'.dependencies] libc = "0.2" diff --git a/src/lib.rs b/src/lib.rs index eedbe41..6b5ac90 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ //! It also provides `remove_dir_contents` and `ensure_empty_dir` //! for both Unix and Windows. +#![cfg_attr(feature = "nightly", feature(io_error_more))] #![deny(missing_debug_implementations)] #![deny(missing_docs)] #![deny(rust_2018_idioms)] diff --git a/src/portable.rs b/src/portable.rs index 11f4deb..4ffb7c7 100644 --- a/src/portable.rs +++ b/src/portable.rs @@ -1,11 +1,13 @@ use std::io; use std::path::Path; -#[cfg(windows)] -use crate::fs::_remove_dir_contents; - -#[cfg(not(windows))] -use crate::unix::_remove_dir_contents; +cfg_if::cfg_if! { + if #[cfg(windows)] { + use crate::fs::_remove_dir_contents; + } else { + use crate::unix::_remove_dir_contents; + } +} /// Deletes the contents of `path`, but not the directory iteself. /// @@ -28,26 +30,47 @@ pub fn remove_dir_contents>(path: P) -> io::Result<()> { /// a symlink to one). pub fn ensure_empty_dir>(path: P) -> io::Result<()> { match std::fs::create_dir(&path) { - Err(e) if e.kind() == io::ErrorKind::AlreadyExists - => remove_dir_contents(path), + Err(e) if e.kind() == io::ErrorKind::AlreadyExists => remove_dir_contents(path), otherwise => otherwise, } } #[cfg(test)] mod test { + use std::fs::{self, File}; + use std::io; + use std::path::PathBuf; + use tempfile::TempDir; + + use crate::ensure_empty_dir; use crate::remove_dir_all; use crate::remove_dir_contents; - use crate::ensure_empty_dir; - use std::fs::{self, File}; - use std::path::PathBuf; - use std::io; - fn expect_failure(k: io::ErrorKind, r: io::Result) -> io::Result<()> { + cfg_if::cfg_if! { + if #[cfg(windows)] { + const ENOTDIR:i32 = winapi::shared::winerror::ERROR_DIRECTORY as i32; + const ENOENT:i32 = winapi::shared::winerror::ERROR_FILE_NOT_FOUND as i32; + } else { + const ENOTDIR:i32 = libc::ENOTDIR; + const ENOENT:i32 = libc::ENOENT; + } + } + + /// Expect a particular sort of failure + fn expect_failure(n: &[i32], r: io::Result) -> io::Result<()> { match r { - Err(e) if e.kind() == k => Ok(()), - Err(e) => Err(e), + Err(e) + if n.iter() + .map(|n| Option::Some(*n)) + .any(|n| n == e.raw_os_error()) => + { + Ok(()) + } + Err(e) => { + println!("{e} {:?}, {:?}, {:?}", e.raw_os_error(), e.kind(), n); + Err(e) + } Ok(_) => Err(io::Error::new( io::ErrorKind::Other, "unexpected success".to_string(), @@ -61,6 +84,7 @@ mod test { file: PathBuf, } + /// Create test setup: t.mkdir/file all in a tempdir. fn prep() -> Result { let tmp = TempDir::new()?; let ours = tmp.path().join("t.mkdir"); @@ -68,21 +92,25 @@ mod test { fs::create_dir(&ours)?; File::create(&file)?; File::open(&file)?; - Ok(Prep { _tmp: tmp, ours, file }) + Ok(Prep { + _tmp: tmp, + ours, + file, + }) } #[test] fn mkdir_rm() -> Result<(), io::Error> { let p = prep()?; - expect_failure(io::ErrorKind::Other, remove_dir_contents(&p.file))?; + expect_failure(&[ENOTDIR], remove_dir_contents(&p.file))?; remove_dir_contents(&p.ours)?; - expect_failure(io::ErrorKind::NotFound, File::open(&p.file))?; + expect_failure(&[ENOENT], File::open(&p.file))?; remove_dir_contents(&p.ours)?; remove_dir_all(&p.ours)?; - expect_failure(io::ErrorKind::NotFound, remove_dir_contents(&p.ours))?; + expect_failure(&[ENOENT], remove_dir_contents(&p.ours))?; Ok(()) } @@ -90,10 +118,10 @@ mod test { fn ensure_rm() -> Result<(), io::Error> { let p = prep()?; - expect_failure(io::ErrorKind::Other, ensure_empty_dir(&p.file))?; + expect_failure(&[ENOTDIR], ensure_empty_dir(&p.file))?; ensure_empty_dir(&p.ours)?; - expect_failure(io::ErrorKind::NotFound, File::open(&p.file))?; + expect_failure(&[ENOENT], File::open(&p.file))?; ensure_empty_dir(&p.ours)?; remove_dir_all(&p.ours)?;