diff --git a/Cargo.lock b/Cargo.lock index 5a41395..cc71d40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,6 +68,22 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + [[package]] name = "heck" version = "0.4.1" @@ -124,6 +140,12 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "lock_api" version = "0.4.12" @@ -202,6 +224,7 @@ dependencies = [ "serde", "serde-xml-rs", "serde_json", + "tempfile", "thiserror", ] @@ -295,6 +318,19 @@ dependencies = [ "bitflags", ] +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "ryu" version = "1.0.18" @@ -373,6 +409,18 @@ version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys", +] + [[package]] name = "thiserror" version = "1.0.61" @@ -468,6 +516,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.52.5" diff --git a/Cargo.toml b/Cargo.toml index 1c3866e..f513343 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,6 @@ serde = { version = "1.0.203", features = ["derive"] } serde-xml-rs = "0.6.0" serde_json = "1.0.117" thiserror = "1.0.61" + +[dev-dependencies] +tempfile = "3.10.1" diff --git a/src/errors.rs b/src/errors.rs index 565d885..30ec5c0 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -7,18 +7,22 @@ use thiserror::Error; #[non_exhaustive] pub enum Error { /// The file is not a XML file. - #[error("File is not a XML file.")] - InvalidFileType, + #[error("File {:?} is not a XML file.", 0)] + InvalidFileType(PathBuf), /// The file was not found at the specififed path. #[error("File was not found at the specified path: {:?}.", 0)] FileNotFound(PathBuf), - /// An unknown error occurred. + /// An io error occurred. #[error(transparent)] IO(#[from] std::io::Error), /// A parsing error occurred. #[error(transparent)] ParsingError(#[from] serde_xml_rs::Error), + + /// An unknown error occurred. + #[error("Unknown error")] + Unknown, } diff --git a/src/lib.rs b/src/lib.rs index df54742..800727d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,9 +25,7 @@ use crate::native::{ /// assert!(native.sites.len() >= 1, "Vector length is less than 1"); /// ``` pub fn parse_site_native_file(xml_path: &Path) -> Result { - if !xml_path.exists() { - return Err(Error::FileNotFound(xml_path.to_path_buf())); - } + check_valid_xml_file(xml_path)?; let xml_file = read_to_string(xml_path)?; let native = parse_site_native_string(&xml_file)?; @@ -406,9 +404,7 @@ pub fn parse_site_native_string(xml_str: &str) -> Result { /// assert!(native.patients.len() >= 1, "Vector length is less than 1"); /// ``` pub fn parse_subject_native_file(xml_path: &Path) -> Result { - if !xml_path.exists() { - return Err(Error::FileNotFound(xml_path.to_path_buf())); - } + check_valid_xml_file(xml_path)?; let xml_file = read_to_string(xml_path)?; let native = parse_subject_native_string(&xml_file)?; @@ -617,9 +613,7 @@ pub fn parse_subject_native_string(xml_str: &str) -> Result= 1, "Vector length is less than 1"); /// ``` pub fn parse_user_native_file(xml_path: &Path) -> Result { - if !xml_path.exists() { - return Err(Error::FileNotFound(xml_path.to_path_buf())); - } + check_valid_xml_file(xml_path)?; let xml_file = read_to_string(xml_path)?; let native = parse_user_native_string(&xml_file)?; @@ -792,3 +786,88 @@ pub fn parse_user_native_string(xml_str: &str) -> Result { Ok(native) } + +fn check_valid_xml_file(xml_path: &Path) -> Result<(), Error> { + if !xml_path.exists() { + return Err(Error::FileNotFound(xml_path.to_path_buf())); + } + + if let Some(extension) = xml_path.extension() { + if extension != "xml" { + return Err(Error::InvalidFileType(xml_path.to_owned())); + } + } else { + return Err(Error::Unknown); + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use tempfile::{tempdir, Builder}; + + #[test] + fn test_site_file_not_found_error() { + let dir = tempdir().unwrap().path().to_path_buf(); + let result = parse_site_native_file(&dir); + assert!(result.is_err()); + assert!(matches!(result, Err(Error::FileNotFound(_)))); + } + + #[test] + fn test_site_invaid_file_type_error() { + let file = Builder::new() + .prefix("test") + .suffix(".csv") + .tempfile() + .unwrap(); + let result = parse_site_native_file(file.path()); + + assert!(result.is_err()); + assert!(matches!(result, Err(Error::InvalidFileType(_)))); + } + + #[test] + fn test_subject_file_not_found_error() { + let dir = tempdir().unwrap().path().to_path_buf(); + let result = parse_subject_native_file(&dir); + assert!(result.is_err()); + assert!(matches!(result, Err(Error::FileNotFound(_)))); + } + + #[test] + fn test_subject_invaid_file_type_error() { + let file = Builder::new() + .prefix("test") + .suffix(".csv") + .tempfile() + .unwrap(); + let result = parse_subject_native_file(file.path()); + + assert!(result.is_err()); + assert!(matches!(result, Err(Error::InvalidFileType(_)))); + } + + #[test] + fn test_user_file_not_found_error() { + let dir = tempdir().unwrap().path().to_path_buf(); + let result = parse_user_native_file(&dir); + assert!(result.is_err()); + assert!(matches!(result, Err(Error::FileNotFound(_)))); + } + + #[test] + fn test_user_invaid_file_type_error() { + let file = Builder::new() + .prefix("test") + .suffix(".csv") + .tempfile() + .unwrap(); + let result = parse_user_native_file(file.path()); + + assert!(result.is_err()); + assert!(matches!(result, Err(Error::InvalidFileType(_)))); + } +}