diff --git a/.gitignore b/.gitignore index 3bfb83c..e9a4d5c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /target/ **/*.rs.bk Cargo.lock +/tests/7z /tests/testing-plugins /tests/loot_api* /ffi/include/ diff --git a/.travis.yml b/.travis.yml index e852cc6..4f38760 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,9 @@ before_script: - 7z x loot_api-0.13.8-0-g47797cc_dev-win64.7z - mv loot_api-0.13.8-0-g47797cc_dev-win64 loot_api_win64 + - wget https://7-zip.org/a/7z1805-extra.7z + - 7z x 7z1805-extra.7z -o7z + - cd .. script: diff --git a/appveyor.yml b/appveyor.yml index d2a909d..1b0d609 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -31,6 +31,9 @@ install: - 7z x loot_api_win64.7z - mv loot_api-0.13.8-0-g47797cc_dev-win64 loot_api_win64 + - ps: (New-Object Net.WebClient).DownloadFile('https://7-zip.org/a/7z1805-extra.7z', "$PWD/7z.7z") + - ps: 7z x "$PWD/7z.7z" -o7z + - cd .. build: false diff --git a/src/error.rs b/src/error.rs index 7c73146..2c78463 100644 --- a/src/error.rs +++ b/src/error.rs @@ -53,7 +53,7 @@ impl fmt::Display for Error { ), Error::PeParsingError(p, e) => write!( f, - "An error was encountered while reading the file version field of \"{}\": {}", + "An error was encountered while reading the version fields of \"{}\": {}", p.display(), e ), diff --git a/src/version.rs b/src/version.rs index 89dbe14..6b86886 100644 --- a/src/version.rs +++ b/src/version.rs @@ -1,6 +1,7 @@ use std::cmp::Ordering; use std::path::Path; +use pelite::image::VS_FIXEDFILEINFO; use pelite::resources::version_info::VersionInfo; use pelite::resources::FindError; use pelite::FileMap; @@ -29,21 +30,40 @@ pub struct Version { impl Version { pub fn read_file_version(file_path: &Path) -> Result { + Self::read_version(file_path, |f| { + format!( + "{}.{}.{}.{}", + f.dwFileVersion.Major, + f.dwFileVersion.Minor, + f.dwFileVersion.Patch, + f.dwFileVersion.Build + ) + }) + } + + pub fn read_product_version(file_path: &Path) -> Result { + Self::read_version(file_path, |f| { + format!( + "{}.{}.{}.{}", + f.dwProductVersion.Major, + f.dwProductVersion.Minor, + f.dwProductVersion.Patch, + f.dwProductVersion.Build + ) + }) + } + + fn read_version String>( + file_path: &Path, + formatter: F, + ) -> Result { let file_map = FileMap::open(file_path).map_err(|e| Error::IoError(file_path.to_path_buf(), e))?; let version_info = get_pe_version_info(file_map.as_ref()) .map_err(|e| Error::PeParsingError(file_path.to_path_buf(), e.into()))?; if let Some(fixed_file_info) = version_info.fixed() { - let version = format!( - "{}.{}.{}.{}", - fixed_file_info.dwFileVersion.Major, - fixed_file_info.dwFileVersion.Minor, - fixed_file_info.dwFileVersion.Patch, - fixed_file_info.dwFileVersion.Build - ); - - Ok(Version::from(version.as_str())) + Ok(Version::from(formatter(fixed_file_info).as_str())) } else { Ok(Version::from("")) } @@ -143,7 +163,7 @@ fn pad_release_ids(ids1: &[Identifier], ids2: &[Identifier]) -> (Vec #[cfg(test)] mod tests { - mod empty { + mod constructors { use super::super::*; #[test] @@ -195,9 +215,64 @@ mod tests { fn version_read_file_version_should_error_with_path_if_the_file_is_not_an_executable() { let error = Version::read_file_version(Path::new("Cargo.toml")).unwrap_err(); - assert_eq!("An error was encountered while reading the file version field of \"Cargo.toml\": bad magic", error.to_string()); + assert_eq!("An error was encountered while reading the version fields of \"Cargo.toml\": bad magic", error.to_string()); } + #[test] + fn version_read_product_version_should_read_the_file_version_field_of_a_32_bit_executable() + { + let version = Version::read_product_version(Path::new("tests/7z/7za.exe")).unwrap(); + + assert_eq!( + version.release_ids, + vec![ + Identifier::Numeric(18), + Identifier::Numeric(5), + Identifier::Numeric(0), + Identifier::Numeric(0), + ] + ); + assert!(version.pre_release_ids.is_empty()); + } + + #[test] + fn version_read_product_version_should_read_the_file_version_field_of_a_64_bit_executable() + { + let version = Version::read_product_version(Path::new("tests/7z/x64/7za.exe")).unwrap(); + + assert_eq!( + version.release_ids, + vec![ + Identifier::Numeric(18), + Identifier::Numeric(5), + Identifier::Numeric(0), + Identifier::Numeric(0), + ] + ); + assert!(version.pre_release_ids.is_empty()); + } + + #[test] + fn version_read_product_version_should_error_with_path_if_path_does_not_exist() { + let error = Version::read_product_version(Path::new("missing")).unwrap_err(); + + assert!( + error + .to_string() + .starts_with("An error was encountered while accessing the path \"missing\":") + ); + } + + #[test] + fn version_read_product_version_should_error_with_path_if_the_file_is_not_an_executable() { + let error = Version::read_product_version(Path::new("Cargo.toml")).unwrap_err(); + + assert_eq!("An error was encountered while reading the version fields of \"Cargo.toml\": bad magic", error.to_string()); + } + } + + mod empty { + use super::super::*; #[test] fn version_eq_an_empty_string_should_equal_an_empty_string() { assert_eq!(Version::from(""), Version::from(""));