diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 16f0427..e7fde6b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -3,8 +3,12 @@ name: Rust on: push: branches: [ main ] + paths-ignore: + - '**.md' pull_request: branches: [ main ] + paths-ignore: + - '**.md' env: CARGO_TERM_COLOR: always @@ -24,16 +28,16 @@ jobs: run: cargo build --no-default-features - name: Build lib with native-tls - run: cargo build --no-default-features --features lib-core,native-tls + run: cargo build --no-default-features --features lib-core,native-tls && cargo tree --no-default-features --features lib-core,native-tls | grep -q rustls && {exit 1} || echo "success" - name: Build lib with rustls - run: cargo build --no-default-features --features lib-core,rustls + run: cargo build --no-default-features --features lib-core,rustls && cargo tree --no-default-features --features lib-core,rustls | grep -q native-tls && {exit 1} || echo "success" - name: Build lib with all features run: cargo build --all-features - - name: Run test for lib + s3 features (s3 for doc compilation tests) - run: cargo test --features s3 + - name: Run test for lib + all optionals features + run: cargo test --all-features - name: Run format check run: cargo fmt --check diff --git a/src/oneio/mod.rs b/src/oneio/mod.rs index e69dd43..69c504e 100644 --- a/src/oneio/mod.rs +++ b/src/oneio/mod.rs @@ -169,3 +169,29 @@ pub fn get_writer(path: &str) -> Result, OneIoError> { _ => Ok(Box::new(BufWriter::new(output_file))), } } + +/// Check if a file or directory exists. +/// +/// This function takes a path as an argument and returns a `Result` indicating whether the file or directory at the given path exists or not. +/// +/// # Examples +/// +/// ```rust +/// use crate::oneio::exists; +/// +/// match exists("path/to/file.txt") { +/// Ok(true) => println!("File exists."), +/// Ok(false) => println!("File does not exist."), +/// Err(error) => eprintln!("An error occurred: {}", error), +/// } +/// ``` +/// +/// # Errors +/// +/// This function may return an `OneIoError` if there is an error accessing the file system or if the `remote` feature is enabled and there is an error +pub fn exists(path: &str) -> Result { + #[cfg(feature = "remote")] + return remote::remote_file_exists(path); + #[cfg(not(feature = "remote"))] + Ok(std::path::Path::new(path).exists()) +} diff --git a/src/oneio/remote.rs b/src/oneio/remote.rs index bd1d58d..d0a2348 100644 --- a/src/oneio/remote.rs +++ b/src/oneio/remote.rs @@ -229,3 +229,41 @@ pub(crate) fn get_reader_raw_remote(path: &str) -> Result, Ok(raw_reader) } + +/// Check if a remote or local file exists. +/// +/// # Arguments +/// +/// * `path` - The path of the file to check. +/// +/// # Returns +/// +/// Returns a `Result` containing a `bool` indicating whether the file exists or not. If the path is not supported, +/// an `Err` variant with an `OneIoError::NotSupported` error is returned. If there is an error during the file check, +/// an `Err` variant with an `OneIoError` is returned. +pub(crate) fn remote_file_exists(path: &str) -> Result { + match get_protocol(path) { + Some(protocol) => match protocol.as_str() { + "http" | "https" => { + let client = reqwest::blocking::Client::builder() + .timeout(std::time::Duration::from_secs(2)) + .build()?; + let res = client.head(path).send()?; + Ok(res.status().is_success()) + } + #[cfg(feature = "s3")] + "s3" => { + let (bucket, path) = crate::oneio::s3::s3_url_parse(path)?; + let res = crate::oneio::s3::s3_exists(bucket.as_str(), path.as_str())?; + Ok(res) + } + _ => { + return Err(OneIoError::NotSupported(path.to_string())); + } + }, + None => { + // check if local file exists + Ok(std::path::Path::new(path).exists()) + } + } +} diff --git a/tests/oneio_test.rs b/tests/oneio_test.rs index 83947d5..5bb7ed4 100644 --- a/tests/oneio_test.rs +++ b/tests/oneio_test.rs @@ -155,6 +155,9 @@ fn test_read_json_struct() { fn test_read_404() { let reader = oneio::get_reader("https://spaces.bgpkit.org/oneio/test_data_NOT_EXIST.json"); assert!(reader.is_err()); + assert!(!oneio::exists("https://spaces.bgpkit.org/oneio/test_data_NOT_EXIST.json").unwrap()); + let reader = oneio::get_reader("https://spaces.bgpkit.org/oneio/test_data.json"); assert!(reader.is_ok()); + assert!(oneio::exists("https://spaces.bgpkit.org/oneio/test_data.json").unwrap()); }