From 493f75441fa02aea3529c1c30b1dae7e1614bb1f Mon Sep 17 00:00:00 2001 From: AdmiringWorm Date: Sun, 11 Apr 2021 14:40:13 +0200 Subject: [PATCH] feat(choco): add ability to generate nuspec files --- Cargo.lock | 105 +++ aer_data/src/metadata.rs | 17 + aer_data/src/metadata/chocolatey.rs | 105 ++- aer_upd/Cargo.toml | 8 +- aer_upd/src/generators.rs | 19 + aer_upd/src/generators/chocolatey.rs | 1091 ++++++++++++++++++++++++ aer_upd/src/lib.rs | 1 + aer_upd/test-data/.editorconfig | 2 + aer_upd/test-data/nuspecs/empty.nuspec | 15 + aer_upd/test-data/nuspecs/full.nuspec | 51 ++ aer_version/src/lib.rs | 14 + 11 files changed, 1423 insertions(+), 5 deletions(-) create mode 100644 aer_upd/src/generators.rs create mode 100644 aer_upd/src/generators/chocolatey.rs create mode 100644 aer_upd/test-data/.editorconfig create mode 100644 aer_upd/test-data/nuspecs/empty.nuspec create mode 100644 aer_upd/test-data/nuspecs/full.nuspec diff --git a/Cargo.lock b/Cargo.lock index c623870..aae65a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,12 +70,15 @@ version = "0.1.0" dependencies = [ "aer_data", "aer_web", + "assert_fs", "lazy_static", "log", + "predicates", "rstest", "serde", "serde_json", "toml", + "xml-rs", ] [[package]] @@ -134,6 +137,20 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "assert_fs" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3203d5bb9979ac7210f01a150578ebafef6f08b55e79f6db32673c0977b94340" +dependencies = [ + "doc-comment", + "globwalk", + "predicates", + "predicates-core", + "predicates-tree", + "tempfile", +] + [[package]] name = "atty" version = "0.2.14" @@ -302,6 +319,17 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" +[[package]] +name = "crossbeam-utils" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", +] + [[package]] name = "difference" version = "2.0.0" @@ -517,6 +545,30 @@ version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" +[[package]] +name = "globset" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c152169ef1e421390738366d2f796655fec62621dabbd0fd476f905934061e4a" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags", + "ignore", + "walkdir", +] + [[package]] name = "h2" version = "0.3.2" @@ -691,6 +743,24 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "ignore" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b287fb45c60bb826a0dc68ff08742b9d88a2fea13d6e0c286b3172065aaf878c" +dependencies = [ + "crossbeam-utils", + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + [[package]] name = "indexmap" version = "1.6.2" @@ -1471,6 +1541,15 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.19" @@ -1761,6 +1840,15 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thread_local" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" +dependencies = [ + "once_cell", +] + [[package]] name = "time" version = "0.1.44" @@ -1993,6 +2081,17 @@ dependencies = [ "libc", ] +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + [[package]] name = "want" version = "0.3.0" @@ -2162,6 +2261,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "xml-rs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" + [[package]] name = "xml5ever" version = "0.16.1" diff --git a/aer_data/src/metadata.rs b/aer_data/src/metadata.rs index 03abc14..e522937 100644 --- a/aer_data/src/metadata.rs +++ b/aer_data/src/metadata.rs @@ -50,6 +50,7 @@ impl PartialEq for Description { #[non_exhaustive] pub struct PackageMetadata { package_source_url: Option, + icon_url: Option, /// The identifier of the package id: String, @@ -118,6 +119,7 @@ impl PackageMetadata { pub fn new>(id: T) -> PackageMetadata { PackageMetadata { package_source_url: None, + icon_url: None, id: id.as_ref().to_string(), maintainers: crate::defaults::maintainer(), summary: String::new(), @@ -169,6 +171,13 @@ impl PackageMetadata { } } + /// The direct url to the icon of the software. + /// Typically hosted in a repository, and linked to with jsdelivr, rawhack, + /// etc. + pub fn icon_url(&self) -> &Option { + &self.icon_url + } + /// Returns the url to the landing page of the software. pub fn project_url(&self) -> &Url { &self.project_url @@ -225,6 +234,13 @@ impl PackageMetadata { Ok(()) } + /// Allows setting the url to the software icon for this package. + /// Will return [url::ParseError] if the specified url is not a url. + pub fn set_icon_url>(&mut self, url: U) -> Result<(), url::ParseError> { + self.icon_url = Some(Url::parse(url.as_ref())?); + Ok(()) + } + /// Allows setting the url to the project (usually the home page of the /// software). Will return [url::ParseError] if the specified url is not /// a url. @@ -271,6 +287,7 @@ mod tests { fn new_should_create_default_metadata_with_expected_values() { let expected = PackageMetadata { package_source_url: None, + icon_url: None, id: "test-package".to_owned(), maintainers: crate::defaults::maintainer(), project_url: crate::defaults::url(), diff --git a/aer_data/src/metadata/chocolatey.rs b/aer_data/src/metadata/chocolatey.rs index cc7cd5e..9fb43ea 100644 --- a/aer_data/src/metadata/chocolatey.rs +++ b/aer_data/src/metadata/chocolatey.rs @@ -11,6 +11,7 @@ use std::collections::HashMap; use std::fmt::Display; use std::path::{Path, PathBuf}; +use aer_version::chocolatey::ChocoVersion; use aer_version::Versions; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; @@ -113,6 +114,15 @@ pub struct ChocolateyMetadata { /// [package metadata struct]: PackageMetadata pub package_source_url: Option, + /// The direct url to the icon of the software. + /// Typically hosted in a repository, and linked to with jsdelivr, rawhack, + /// etc. + pub icon_url: Option, + + /// The url to the mailing list of the software, this can also be a link to + /// the forum of the software. + pub mailing_list_url: Option, + /// The full url to the license of the software. The location should be a /// public place where the license can be viewed without the need to /// download it. @@ -168,7 +178,7 @@ pub struct ChocolateyMetadata { pub release_notes: Option, #[cfg_attr(feature = "serialize", serde(default))] - dependencies: HashMap, + dependencies: HashMap>, #[cfg_attr(feature = "serialize", serde(default))] files: HashMap, @@ -244,7 +254,7 @@ impl ChocolateyMetadata { /// installed when chocolatey installs the package. This is required by /// us to both specify the identifier of the dependency, and a minimum /// version. - pub fn dependencies(&self) -> &HashMap { + pub fn dependencies(&self) -> &HashMap> { &self.dependencies } @@ -315,7 +325,7 @@ impl ChocolateyMetadata { // TODO: Change version.as_ref() to version when dependency is updated self.dependencies.insert( id.as_ref().into(), - Versions::parse(version.as_ref()).unwrap(), + ChocoVersion::parse(version.as_ref()).ok(), ); } @@ -415,6 +425,8 @@ impl Default for ChocolateyMetadata { project_url: None, project_source_url: None, package_source_url: None, + icon_url: None, + mailing_list_url: None, license_url: None, title: None, copyright: None, @@ -453,6 +465,8 @@ impl DataUpdater for ChocolateyMetadata { /// empty with the values from [PackageMetadata::project_source_url]. /// - Update the [package_source_url][Self::package_source_url] if it is /// empty with the values from [PackageMetadata::package_source_url]. + /// - Update the [icon_url][Self::icon_url] if it is empty with the values + /// from [PackageMetadata::icon_url]. /// - Update the [license_url][Self::license_url] if it is empty with the /// values from [PackageMetadata]::license_url. This will only be /// automatically set if the global metadata also have a url specified, or @@ -485,6 +499,10 @@ impl DataUpdater for ChocolateyMetadata { self.package_source_url = from.package_source_url.clone(); } + if self.icon_url.is_none() { + self.icon_url = from.icon_url.clone(); + } + if self.license_url.is_none() { if let Some(license) = from.license().license_url() { self.license_url = Some(Url::parse(license).unwrap()); @@ -514,6 +532,8 @@ impl DataUpdater for ChocolateyMetadata { /// same as the global [PackageMetadata::project_source_url] variable. /// - Remove the [package_source_url][Self::package_source_url] if it is the /// same as the global [PackageMetadata::package_source_url] variable. + /// - Remove the [icon_url][Self::icon_url] if it is the same as the global + /// [PackageMetadata::icon_url] variable. /// - Remove the [license_url][Self::license_url] if it is the same as the /// global [PackageMetadata::license] variable. /// - Remove the tag matching the automatically generated identifier. @@ -552,6 +572,14 @@ impl DataUpdater for ChocolateyMetadata { } } + if let Some(ref url) = self.icon_url { + if let Some(ref global_url) = from.icon_url() { + if url == global_url { + self.icon_url = None; + } + } + } + if let Some(ref url) = self.license_url { if let Some(license_url) = from.license().license_url() { if url.as_ref() == license_url { @@ -661,6 +689,8 @@ mod tests { project_url: None, project_source_url: None, package_source_url: None, + icon_url: None, + mailing_list_url: None, license_url: None, title: None, copyright: None, @@ -1178,6 +1208,75 @@ mod tests { assert_eq!(actual, expected); } + #[test] + fn update_from_should_set_expected_icon_url() { + let mut rng = thread_rng(); + let url = URLS.choose(&mut rng).unwrap(); + let mut expected = ChocolateyMetadata::default(); + expected.project_url = Some(crate::defaults::url()); + expected.icon_url = Some(url.clone()); + let mut pkg = PackageMetadata::default(); + pkg.set_icon_url(url).unwrap(); + pkg.maintainers.clear(); + + let mut actual = ChocolateyMetadata::default(); + actual.update_from(pkg); + + assert_eq!(actual, expected); + } + + #[test] + fn update_from_should_not_replace_existing_icon_url() { + let mut rng = thread_rng(); + let url = URLS.choose(&mut rng).unwrap(); + let mut expected = ChocolateyMetadata::default(); + expected.icon_url = Some(url.clone()); + expected.project_url = Some(crate::defaults::url()); + let mut pkg = PackageMetadata::default(); + pkg.set_icon_url("https://test-replace.not/icon.png") + .unwrap(); + pkg.maintainers.clear(); + + let mut actual = ChocolateyMetadata::default(); + actual.icon_url = Some(url.clone()); + actual.update_from(pkg); + + assert_eq!(actual, expected); + } + + #[test] + fn reset_same_should_remove_same_icon_url() { + let mut rng = thread_rng(); + let url = URLS.choose(&mut rng).unwrap(); + let expected = ChocolateyMetadata::default(); + let mut pkg = PackageMetadata::default(); + pkg.set_icon_url(url).unwrap(); + pkg.maintainers.clear(); + + let mut actual = ChocolateyMetadata::default(); + actual.icon_url = Some(url.clone()); + actual.reset_same(pkg); + + assert_eq!(actual, expected); + } + + #[test] + fn reset_same_should_not_remove_different_icon_url() { + let mut rng = thread_rng(); + let url = URLS.choose(&mut rng).unwrap(); + let mut expected = ChocolateyMetadata::default(); + expected.icon_url = Some(url.clone()); + let mut pkg = PackageMetadata::default(); + pkg.set_icon_url("https://test-replace.not/icon.png") + .unwrap(); + pkg.maintainers.clear(); + + let mut actual = expected.clone(); + actual.reset_same(pkg); + + assert_eq!(actual, expected); + } + #[test] fn update_from_should_set_expected_license_url() { let mut rng = thread_rng(); diff --git a/aer_upd/Cargo.toml b/aer_upd/Cargo.toml index a44a7ee..00ebd4d 100644 --- a/aer_upd/Cargo.toml +++ b/aer_upd/Cargo.toml @@ -5,8 +5,9 @@ authors = ["AdmiringWorm "] edition = "2018" [features] -default = ["powershell", "toml_data"] -toml_data = ["aer_data/chocolatey", "toml", "aer_data/serialize"] +default = ["powershell", "toml_data", "chocolatey"] +chocolatey = ["aer_data/chocolatey", "xml-rs"] +toml_data = ["toml", "aer_data/serialize"] powershell = ["aer_data/serialize", "lazy_static", "serde_json", "serde"] [dependencies] @@ -17,8 +18,11 @@ log = "0.4.14" serde = { version = "1.0.125", optional = true } serde_json = { version = "1.0.64", optional = true } toml = { version = "0.5.8", optional = true } +xml-rs = { version = "0.8.3", optional = true } [dev-dependencies] +assert_fs = "1.0.1" +predicates = "1.0.7" rstest = "0.7.0" [package.metadata.docs.rs] diff --git a/aer_upd/src/generators.rs b/aer_upd/src/generators.rs new file mode 100644 index 0000000..6e2c03f --- /dev/null +++ b/aer_upd/src/generators.rs @@ -0,0 +1,19 @@ +// Copyright (c) 2021 Kim J. Nordmo and WormieCorp. +// Licensed under the MIT license. See LICENSE.txt file in the project + +//! Contains the necessary logic of creating/generating the files that should be +//! used in a package. + +#![deny(missing_docs)] + +#[cfg(feature = "chocolatey")] +mod chocolatey; + +use std::path::Path; + +/// Defines a trait that allows creating necessary package files based on the +/// wanted generator information. +pub trait PackageGenerator { + /// Generates the files that are expected from the specified metadata. + fn generate(&self, work_dir: &Path) -> Result<(), Box>; +} diff --git a/aer_upd/src/generators/chocolatey.rs b/aer_upd/src/generators/chocolatey.rs new file mode 100644 index 0000000..44f7d8a --- /dev/null +++ b/aer_upd/src/generators/chocolatey.rs @@ -0,0 +1,1091 @@ +// Copyright (c) 2021 Kim J. Nordmo and WormieCorp. +// Licensed under the MIT license. See LICENSE.txt file in the project + +//! Contains logic necessary for creating/generating package files for the +//! Chocolatey package manager. + +#![cfg_attr(docsrs, doc(cfg(any(feature = "chocolatey"))))] +use std::fs; +use std::path::Path; + +use aer_data::prelude::chocolatey::*; +use aer_data::prelude::*; +use xml::writer::XmlEvent; +use xml::EmitterConfig; + +use crate::generators::PackageGenerator; + +/// Generates the xml nuspec file based on the information specified in +/// [ChocolateyMetadata]. +/// +/// It will automatically generate a file directive for a local tools directory. +impl PackageGenerator for ChocolateyMetadata { + fn generate(&self, work_dir: &Path) -> Result<(), Box> { + if self.id().trim().is_empty() { + return Err(std::io::Error::from(std::io::ErrorKind::NotFound).into()); + } + + let work_dir = work_dir.join(self.id()); + + if !work_dir.exists() { + fs::create_dir_all(&work_dir)?; + } else { + for entry in fs::read_dir(&work_dir)? { + let path = entry?.path(); + if path.is_dir() { + fs::remove_dir_all(path)?; + } else { + fs::remove_file(path)?; + } + } + } + + create_nuspec_file(work_dir, self) + } +} + +const XML_TEST_COMMENT: &str = "\ +Do not remove this test for UTF-8: If “Ω” doesn't appear as greek uppercase omega letter +enclosed in quotation marks, you should use an editor that supports UTF-8, not this one."; + +macro_rules! write_element { + ($w:expr, $name:literal, $value:expr) => { + $w.write(::xml::writer::XmlEvent::start_element($name))?; + $w.write(::xml::writer::XmlEvent::characters(&$value.trim()))?; + $w.write(::xml::writer::XmlEvent::end_element())?; + }; + ($w:expr, $name:literal, $($attr_name:literal => $attr_val:expr)+) => { + $w.write(::xml::writer::XmlEvent::start_element($name)$(.attr($attr_name, &$attr_val.trim()))*)?; + $w.write(::xml::writer::XmlEvent::end_element())?; + }; +} + +macro_rules! write_element_cdata { + ($w:expr, $name:literal, $value:expr) => { + $w.write(::xml::writer::XmlEvent::start_element($name))?; + $w.write(::xml::writer::XmlEvent::cdata(&$value.trim()))?; + $w.write(::xml::writer::XmlEvent::end_element())?; + }; +} + +macro_rules! write_element_option { + ($w:expr, $name:literal,$value:expr) => { + if let Some(ref val) = $value { + write_element!($w, $name, val.as_str()); + } + }; +} + +fn create_nuspec_file>( + work_dir: P, + data: &ChocolateyMetadata, +) -> Result<(), Box> { + let file_path = work_dir.as_ref().join(format!("{}.nuspec", data.id())); + let mut file = fs::File::create(file_path)?; + let mut writer = EmitterConfig::new() + .perform_indent(true) + .create_writer(&mut file); + + { + let package_event = XmlEvent::start_element("package") + .default_ns("http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd"); + writer.write(package_event)?; + } + + writer.write(XmlEvent::comment(XML_TEST_COMMENT))?; + writer.write(XmlEvent::start_element("metadata"))?; + + write_element!(writer, "id", data.id()); + { + let version = data.version.to_choco(); + write_element!(writer, "version", version.to_string()); + } + write_element_option!(writer, "packageSourceUrl", data.package_source_url); + write_element!(writer, "owners", data.maintainers().join(",")); + write_element_option!(writer, "title", data.title); + write_element!(writer, "authors", data.authors().join(",")); + write_element_option!(writer, "projectUrl", data.project_url); + write_element_option!(writer, "iconUrl", data.icon_url); + write_element_option!(writer, "copyright", data.copyright); + if let Some(ref url) = data.license_url { + write_element!(writer, "licenseUrl", url.as_str()); + write_element!( + writer, + "requireLicenseAcceptance", + data.require_license_acceptance.to_string() + ); + } + + write_element_option!(writer, "projectSourceUrl", data.project_source_url); + write_element_option!(writer, "docsUrl", data.documentation_url); + write_element_option!(writer, "mailingListUrl", data.mailing_list_url); + write_element_option!(writer, "bugTrackerUrl", data.issues_url); + write_element!(writer, "tags", data.tags().join(" ")); + write_element_option!(writer, "summary", data.summary); + + if let Description::Text(ref text) = data.description() { + write_element_cdata!(writer, "description", text); + } + // Not decided if description should be loaded at this point, or before calling + // the generate function + + write_element_option!(writer, "releaseNotes", data.release_notes); + + if !data.dependencies().is_empty() { + writer.write(XmlEvent::start_element("dependencies"))?; + for (id, version) in data.dependencies() { + if let Some(version) = version { + write_element!(writer, + "dependency", + "id" => id + "version" => &version.to_string()); + } else { + write_element!(writer, "dependency", "id" => id); + } + } + writer.write(XmlEvent::end_element())?; // End of + } + + writer.write(XmlEvent::end_element())?; // End of + + writer.write(XmlEvent::start_element("files"))?; + const DEFAULT_FILE: &str = if cfg!(windows) { + "tools\\**" + } else { + "tools/**" + }; + + write_element!(writer, + "file", + "src" => DEFAULT_FILE + "target" => "tools"); + for (src, target) in data.files() { + let src = if cfg!(windows) { + src.to_string_lossy().replace("/", "\\") + } else { + src.to_string_lossy().replace("\\", "/") + }; + + if src != DEFAULT_FILE { + write_element!(writer, "file", "src" => src "target" => target); + } + } + + writer.write(XmlEvent::end_element())?; // End of + writer.write(XmlEvent::end_element())?; // End of + + Ok(()) +} + +#[cfg(test)] +mod tests { + use aer_data::prelude::*; + use assert_fs::fixture::TempDir; + use assert_fs::prelude::*; + use predicates::prelude::*; + use rstest::rstest; + + use super::*; + + mod metadata { + use super::*; + + #[test] + fn generate_should_create_expected_work_directory() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test", true); + + meta.generate(temp.path()).unwrap(); + + temp.child("test") + .assert(predicate::path::exists().and(predicates::path::is_dir())); + temp.close().unwrap(); + } + + #[test] + fn generate_should_clean_existing_files() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test", true); + let input_file = temp.child("test/non.nuspec"); + input_file.touch().unwrap(); + + meta.generate(temp.path()).unwrap(); + + input_file.assert(predicate::path::missing()); + temp.close().unwrap(); + } + + #[test] + fn generate_should_clean_existing_directories() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test", true); + let input_dir = temp.child("test/tools"); + input_dir.create_dir_all().unwrap(); + + meta.generate(temp.path()).unwrap(); + + input_dir.assert(predicate::path::missing()); + temp.close().unwrap(); + } + + #[test] + #[should_panic = "Kind(NotFound)"] + fn generate_should_panic_if_identifier_is_empty() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::default(); + + let r = meta.generate(temp.path()); + temp.close().unwrap(); + r.unwrap(); + } + + #[test] + fn generate_should_create_nuspec_file() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test-package", true); + + meta.generate(temp.path()).unwrap(); + + temp.child("test-package/test-package.nuspec") + .assert(predicate::path::exists().and(predicate::path::is_file())); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_package_element() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test-package", true); + + meta.generate(temp.path()).unwrap(); + + temp.child("test-package/test-package.nuspec").assert( + predicate::path::exists().and( + predicate::str::contains(r#""#).and( + predicate::str::contains("")) + .from_utf8().from_file_path())); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_test_comment() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test", true); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::path::exists().and( + predicate::str::contains(format!("", XML_TEST_COMMENT)) + .from_utf8() + .from_file_path(), + ), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_metadata_element() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test", true); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::path::exists().and( + predicate::str::contains("") + .and(predicate::str::contains("")) + .from_utf8() + .from_file_path(), + ), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_identifier_element() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("awesome-souce", true); + + meta.generate(temp.path()).unwrap(); + + temp.child("awesome-souce/awesome-souce.nuspec").assert( + predicate::path::exists().and( + predicate::str::contains("awesome-souce") + .from_utf8() + .from_file_path(), + ), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_version_element() { + let temp = TempDir::new().unwrap(); + let mut meta = ChocolateyMetadata::with_id("test", true); + meta.version = SemVersion::parse("5.2.1-alpha.66+99").unwrap().into(); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("5.2.1-alpha0066") + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_package_source_url() { + let temp = TempDir::new().unwrap(); + let mut meta = ChocolateyMetadata::with_id("test", true); + meta.package_source_url = + Some(Url::parse("https://github.com/AdmiringWorm/chocolatey-packages").unwrap()); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("https://github.com/AdmiringWorm/chocolatey-packages") + .from_utf8() + .from_file_path() + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_not_create_package_source_url() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test", true); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("") + .not() + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_owners() { + let temp = TempDir::new().unwrap(); + let mut pkg = PackageMetadata::new("test"); + pkg.set_maintainers(&["AdmiringWorm", "gep13"]); + let mut meta = ChocolateyMetadata::default(); + meta.update_from(pkg); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("AdmiringWorm,gep13") + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_title() { + let temp = TempDir::new().unwrap(); + let mut meta = ChocolateyMetadata::with_id("test", true); + meta.set_title("Test Package"); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("Test Package") + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_not_create_title() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test", true); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("") + .not() + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_authors() { + let temp = TempDir::new().unwrap(); + let pkg = PackageMetadata::new("test"); + let mut meta = ChocolateyMetadata::with_authors(&[ + "Vivek Élodie Michaelson", + "Gostislav Wigburg Asìs", + "Ørjan Petterson", + ]); + meta.update_from(pkg); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains( + "<authors>Vivek Élodie Michaelson,Gostislav Wigburg Asìs,Ørjan \ + Petterson</authors>", + ) + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + #[test] + fn generate_should_create_project_url() { + let temp = TempDir::new().unwrap(); + let mut meta = ChocolateyMetadata::with_id("test", true); + meta.project_url = Some(Url::parse("https://test.com").unwrap()); + meta.generate(temp.path()).unwrap(); + temp.child("test/test.nuspec").assert( + predicate::str::contains("<projectUrl>https://test.com/</projectUrl>") + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + #[test] + fn generate_should_not_create_project_url() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test", true); + meta.generate(temp.path()).unwrap(); + temp.child("test/test.nuspec").assert( + predicate::str::contains("<projectUrl>") + .not() + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_icon_url() { + let temp = TempDir::new().unwrap(); + let mut pkg = PackageMetadata::new("test"); + pkg.set_icon_url("https://example.ci/icon.png").unwrap(); + let mut meta = ChocolateyMetadata::default(); + meta.update_from(pkg); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<iconUrl>https://example.ci/icon.png</iconUrl>") + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_not_create_icon_url() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test", true); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<iconUrl>") + .not() + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + #[test] + fn generate_should_create_copyright() { + let temp = TempDir::new().unwrap(); + let mut meta = ChocolateyMetadata::with_id("test", true); + meta.set_copyright("Awesome copyright"); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<copyright>Awesome copyright</copyright>") + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_not_create_copyright() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test", true); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<copyright>") + .not() + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_license_url() { + let temp = TempDir::new().unwrap(); + let mut meta = ChocolateyMetadata::with_id("test", true); + meta.license_url = Some(Url::parse("https://chocolatey.com/license").unwrap()); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<licenseUrl>https://chocolatey.com/license</licenseUrl>") + .and(predicate::str::contains( + "<requireLicenseAcceptance>true</requireLicenseAcceptance>", + )) + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_require_license_acceptance_to_false() { + let temp = TempDir::new().unwrap(); + let mut meta = ChocolateyMetadata::with_id("test", true); + meta.license_url = Some(Url::parse("https://chocolatey.org").unwrap()); + meta.require_license_acceptance = false; + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<licenseUrl>https://chocolatey.org/</licenseUrl>") + .and(predicate::str::contains( + "<requireLicenseAcceptance>false</requireLicenseAcceptance>", + )) + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[rstest(val, case(true), case(false))] + fn generate_should_not_create_license_url(val: bool) { + let temp = TempDir::new().unwrap(); + let mut meta = ChocolateyMetadata::with_id("test", true); + meta.require_license_acceptance = val; + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<licenseUrl>") + .or(predicate::str::contains("<requireLicenseAcceptance>")) + .not() + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_project_source_url() { + let temp = TempDir::new().unwrap(); + let mut meta = ChocolateyMetadata::with_id("test", true); + meta.project_source_url = + Some(Url::parse("https://www.example.org/source-code").unwrap()); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains( + "<projectSourceUrl>https://www.example.org/source-code</projectSourceUrl>", + ) + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_not_create_project_source_url() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test", true); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<projectSourceUrl>") + .not() + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_documentation_url() { + let temp = TempDir::new().unwrap(); + let mut meta = ChocolateyMetadata::with_id("test", true); + meta.set_title("Test Package"); + meta.documentation_url = Some(Url::parse("https://example.com/docs").unwrap()); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<docsUrl>https://example.com/docs</docsUrl>") + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_not_create_documentation_url() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test", true); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<docsUrl>") + .not() + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_mailing_list_url() { + let temp = TempDir::new().unwrap(); + let mut meta = ChocolateyMetadata::with_id("test", true); + meta.mailing_list_url = Some(Url::parse("https://github.com/mailing-list").unwrap()); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains( + "<mailingListUrl>https://github.com/mailing-list</mailingListUrl>", + ) + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_not_create_mailing_list_url() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test", true); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<mailingListUrl>") + .not() + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_issues_url() { + let temp = TempDir::new().unwrap(); + let mut meta = ChocolateyMetadata::with_id("test", true); + meta.issues_url = Some(Url::parse("https://github.com/WormieCorp/aer/issues").unwrap()); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains( + "<bugTrackerUrl>https://github.com/WormieCorp/aer/issues</bugTrackerUrl>", + ) + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_not_create_issues_url() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test", true); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<bugTrackerUrl>") + .not() + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_issues_tags() { + let temp = TempDir::new().unwrap(); + let mut meta = ChocolateyMetadata::with_id("test", true); + meta.add_tag("cli"); + meta.add_tag("awesome"); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<tags>test cli awesome</tags>") + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_issues_summary() { + let temp = TempDir::new().unwrap(); + let mut meta = ChocolateyMetadata::with_id("test", true); + meta.summary = Some("Awesomeness overloaded".into()); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<summary>Awesomeness overloaded</summary>") + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_not_create_issues_summary() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test", true); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<summary>") + .not() + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_description() { + let text = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor \ + incididunt ut labore et dolore magna aliqua. Leo a diam sollicitudin tempor id \ + eu nisl. Vitae elementum curabitur vitae nunc sed velit dignissim sodales. \ + Dictum non consectetur a erat. Vel risus commodo viverra maecenas accumsan. \ + Ultricies mi eget mauris pharetra et. Pellentesque habitant morbi tristique \ + senectus et netus et malesuada. Cras semper auctor neque vitae. Sit amet \ + consectetur adipiscing elit duis tristique sollicitudin. Mauris commodo quis \ + imperdiet massa tincidunt. Viverra aliquet eget sit amet tellus \ + cras.\n\nMolestie a iaculis at erat pellentesque adipiscing commodo elit at. In \ + nulla posuere sollicitudin aliquam ultrices sagittis orci a scelerisque. Erat \ + nam at lectus urna duis. Leo in vitae turpis massa sed elementum. Iaculis urna \ + id volutpat lacus. Nisl nunc mi ipsum faucibus. Eu augue ut lectus arcu \ + bibendum. Senectus et netus et malesuada. Egestas maecenas pharetra convallis \ + posuere morbi leo urna molestie. Aenean et tortor at risus viverra adipiscing at \ + in tellus. Blandit volutpat maecenas volutpat blandit aliquam etiam erat velit \ + scelerisque. Fames ac turpis egestas sed tempus urna et pharetra. Proin libero \ + nunc consequat interdum varius sit amet mattis. Id faucibus nisl tincidunt eget \ + nullam non nisi est. Lacinia quis vel eros donec ac odio tempor orci dapibus. Mi \ + in nulla posuere sollicitudin aliquam ultrices sagittis. Risus nullam eget felis \ + eget nunc.\n\nSit amet aliquam id diam maecenas ultricies mi eget mauris. \ + Consectetur purus ut faucibus pulvinar. Sit amet tellus cras adipiscing enim. \ + Platea dictumst vestibulum rhoncus est pellentesque. Facilisis leo vel fringilla \ + est ullamcorper eget nulla facilisi. Risus viverra adipiscing at in tellus \ + integer feugiat scelerisque. Vestibulum lorem sed risus ultricies. Aliquam etiam \ + erat velit scelerisque in. Netus et malesuada fames ac turpis egestas integer. \ + Maecenas sed enim ut sem viverra."; + let temp = TempDir::new().unwrap(); + let mut meta = ChocolateyMetadata::with_id("test", true); + meta.set_description_str(text); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains(format!( + "<description><![CDATA[{}]]></description>", + text + )) + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_not_create_description() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test", true); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<description>") + .not() + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_issues_release_notes() { + let temp = TempDir::new().unwrap(); + let mut meta = ChocolateyMetadata::with_id("test", true); + meta.set_release_notes("random release notes"); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<releaseNotes>random release notes</releaseNotes>") + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_not_create_issues_release_notes() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test", true); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<releaseNotes>") + .not() + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_issues_dependencies() { + let temp = TempDir::new().unwrap(); + let mut meta = ChocolateyMetadata::with_id("test", true); + meta.set_dependencies(&[("chocolatey-core.extension", "2.1.0"), ("python3", "")]); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<dependencies>") + .and(predicate::str::contains( + r#"<dependency id="chocolatey-core.extension" version="2.1.0" />"#, + )) + .and(predicate::str::contains(r#"<dependency id="python3" />"#)) + .and(predicate::str::contains("</dependencies>")) + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_not_create_issues_dependencies() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test", true); + + meta.generate(temp.path()).unwrap(); + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<dependencies>") + .or(predicate::str::contains("<dependency")) + .or(predicate::str::contains("<dependencies />")) + .not() + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_always_files() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("test", true); + + meta.generate(temp.path()).unwrap(); + let sep = if cfg!(windows) { '\\' } else { '/' }; + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<files>") + .and(predicate::str::contains(format!( + r#"<file src="tools{}**" target="tools" />"#, + sep + ))) + .and(predicate::str::contains("</files>")) + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_not_create_duplicate_tools_source() { + let temp = TempDir::new().unwrap(); + let mut meta = ChocolateyMetadata::with_id("test", true); + meta.set_files(&[("tools/**", "tools")]); + + meta.generate(temp.path()).unwrap(); + let sep = if cfg!(windows) { '\\' } else { '/' }; + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<files>") + .and( + predicate::str::contains(format!( + r#"<file src="tools{}**" target="tools" />"#, + sep + )) + .count(1), + ) + .and(predicate::str::contains("</files>")) + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_custom_files() { + let temp = TempDir::new().unwrap(); + let mut meta = ChocolateyMetadata::with_id("test", true); + meta.add_file("legal/**", "legal"); + meta.add_file("test\\**", "test"); + + meta.generate(temp.path()).unwrap(); + let sep = if cfg!(windows) { '\\' } else { '/' }; + + temp.child("test/test.nuspec").assert( + predicate::str::contains("<files>") + .and(predicate::str::contains(format!( + r#"<file src="tools{}**" target="tools" />"#, + sep + ))) + .and(predicate::str::contains(format!( + r#"<file src="legal{}**" target="legal" />"#, + sep + ))) + .and(predicate::str::contains(format!( + r#"<file src="test{}**" target="test" />"#, + sep + ))) + .and(predicate::str::contains("</files>")) + .from_utf8() + .from_file_path(), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_expected_empty_nuspec() { + let temp = TempDir::new().unwrap(); + let meta = ChocolateyMetadata::with_id("authy-desktop", true); + + meta.generate(temp.path()).unwrap(); + let sep = if cfg!(windows) { '\\' } else { '/' }; + + temp.child("authy-desktop/authy-desktop.nuspec").assert( + predicate::path::exists() + .and(predicate::path::is_file()) + .and( + predicate::str::similar(format!( + include_str!("../../test-data/nuspecs/empty.nuspec"), + sep = sep + )) + .from_utf8() + .from_file_path(), + ), + ); + temp.close().unwrap(); + } + + #[test] + fn generate_should_create_expected_full_nuspec() { + let temp = TempDir::new().unwrap(); + let mut pkg = PackageMetadata::new("InkScape"); + pkg.set_icon_url("https://cdn.jsdelivr.net/gh/chocolatey-community/chocolatey-coreteampackages@84a3a84e256daa3255c4a896eefbf8f5589fb842/icons/InkScape.svg").unwrap(); + pkg.set_license("https://git.launchpad.net/inkscape/tree/COPYING"); + pkg.set_maintainers(&["chocolatey-community"]); + pkg.set_package_source_url("https://github.com/chocolatey-community/chocolatey-coreteampackages/tree/master/automatic/inkscape").unwrap(); + pkg.set_project_source_url("https://git.launchpad.net/inkscape/tree/") + .unwrap(); + pkg.set_project_url("https://inkscape.org/"); + pkg.summary = "An Open Source vector graphics editor, with capabilities similar to \ + Illustrator, CorelDraw, or Xara X, using the W3C standard Scalable \ + Vector Graphics (SVG) file format." + .into(); + let mut choco = ChocolateyMetadata::with_authors(&["Inkscape developers"]); + choco.add_dependencies("chocolatey-core.extension", "1.3.3"); + choco.add_file("legal\\**", "legal"); + choco.add_file("tools\\**", "tools"); + choco.set_copyright("inkscape.org"); + choco.set_description_str("Inkscape is an open-source vector graphics editor similar to Adobe Illustrator, Corel Draw, Freehand, or Xara X. What sets Inkscape apart is its use of Scalable Vector Graphics (SVG), an open XML-based W3C standard, as the native format. + +Inkscape supports many advanced SVG features (markers, clones, alpha blending, etc.) and great care is taken in designing a streamlined interface. It is very easy to edit nodes, perform complex path operations, trace bitmaps and much more. We also aim to maintain a thriving user and developer community by using open, community-oriented development. + +All Inkscape projects may be exported in formats friendly to web browsers or commercial printer rooms. It is cross-platform, which means it is easy to run on Windows, Mac OS X, and Linux distributions. Visit the Download page to install or share this application now. + +![InkScape](https://i.imgur.com/hvdwGBt.png) + +[More screenshots](https://inkscape.org/en/about/screenshots/). + +## Features + +* Object creation: drawing, shape tools, text tool, bitmaps, clones +* Object manipulation: transformations, z-order operations, grouping, layers, aligment +* Fill and stroke: color selector, color picker tool, copy/paste style, pattern fills, dashed strokes, with many predefined dash patterns, path markers (ending, middle and/or beginning marks, e.g. arrowheads) +* Operations on paths +* Rendering: fully anti-aliased display, alpha transparency support for display and PNG export +* File formats: SVG, PNG, OpenDocument Drawing, DXF, sk1, PDF, EPS and PostScript export formats and more +* Command line options for export and conversions"); + choco.set_release_notes("https://inkscape.org/release/inkscape-1.0.2/#left-column"); + choco.set_title("Inkscape"); + choco.set_tags(&[ + "editor", + "foss", + "cross-platform", + "svg", + "vector-graphics", + "icons", + "graphics", + "export", + "drawing", + "art", + "admin", + ]); + choco.documentation_url = Some(Url::parse("https://inkscape.org/en/learn/").unwrap()); + choco.issues_url = Some(Url::parse("https://bugs.launchpad.net/inkscape").unwrap()); + choco.mailing_list_url = + Some(Url::parse("https://inkscape.org/en/community/mailing-lists/").unwrap()); + choco.require_license_acceptance = false; + choco.version = Versions::parse("1.0.2").unwrap(); + choco.update_from(&pkg); + + choco.generate(temp.path()).unwrap(); + + let sep = if cfg!(windows) { '\\' } else { '/' }; + + temp.child("inkscape/inkscape.nuspec").assert( + predicate::path::exists() + .and(predicate::path::is_file()) + .and( + predicate::str::similar(format!( + include_str!("../../test-data/nuspecs/full.nuspec"), + sep = sep + )) + .from_utf8() + .from_file_path(), + ), + ); + temp.close().unwrap(); + } + } +} diff --git a/aer_upd/src/lib.rs b/aer_upd/src/lib.rs index bbd875e..66f3f1d 100644 --- a/aer_upd/src/lib.rs +++ b/aer_upd/src/lib.rs @@ -13,6 +13,7 @@ )] #![cfg_attr(docsrs, feature(doc_cfg))] +pub mod generators; pub mod parsers; pub mod runners; diff --git a/aer_upd/test-data/.editorconfig b/aer_upd/test-data/.editorconfig new file mode 100644 index 0000000..d92284c --- /dev/null +++ b/aer_upd/test-data/.editorconfig @@ -0,0 +1,2 @@ +[*] +insert_final_newline = false diff --git a/aer_upd/test-data/nuspecs/empty.nuspec b/aer_upd/test-data/nuspecs/empty.nuspec new file mode 100644 index 0000000..2e2cd67 --- /dev/null +++ b/aer_upd/test-data/nuspecs/empty.nuspec @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd"> + <!-- Do not remove this test for UTF-8: If “Ω” doesn't appear as greek uppercase omega letter +enclosed in quotation marks, you should use an editor that supports UTF-8, not this one. --> + <metadata> + <id>authy-desktop</id> + <version>0.0.0</version> + <owners></owners> + <authors></authors> + <tags>authy-desktop</tags> + </metadata> + <files> + <file src="tools{sep}**" target="tools" /> + </files> +</package> \ No newline at end of file diff --git a/aer_upd/test-data/nuspecs/full.nuspec b/aer_upd/test-data/nuspecs/full.nuspec new file mode 100644 index 0000000..6d5f8eb --- /dev/null +++ b/aer_upd/test-data/nuspecs/full.nuspec @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> +<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd"> + <!-- Do not remove this test for UTF-8: If “Ω” doesn't appear as greek uppercase omega letter +enclosed in quotation marks, you should use an editor that supports UTF-8, not this one. --> + <metadata> + <id>inkscape</id> + <version>1.0.2</version> + <packageSourceUrl>https://github.com/chocolatey-community/chocolatey-coreteampackages/tree/master/automatic/inkscape</packageSourceUrl> + <owners>chocolatey-community</owners> + <title>Inkscape + Inkscape developers + https://inkscape.org/ + https://cdn.jsdelivr.net/gh/chocolatey-community/chocolatey-coreteampackages@84a3a84e256daa3255c4a896eefbf8f5589fb842/icons/InkScape.svg + inkscape.org + https://git.launchpad.net/inkscape/tree/COPYING + false + https://git.launchpad.net/inkscape/tree/ + https://inkscape.org/en/learn/ + https://inkscape.org/en/community/mailing-lists/ + https://bugs.launchpad.net/inkscape + inkscape editor foss cross-platform svg vector-graphics icons graphics export drawing art admin + An Open Source vector graphics editor, with capabilities similar to Illustrator, CorelDraw, or Xara X, using the W3C standard Scalable Vector Graphics (SVG) file format. + + https://inkscape.org/release/inkscape-1.0.2/#left-column + + + + + + + + + \ No newline at end of file diff --git a/aer_version/src/lib.rs b/aer_version/src/lib.rs index 01519fe..3a6bcce 100644 --- a/aer_version/src/lib.rs +++ b/aer_version/src/lib.rs @@ -88,6 +88,20 @@ impl Display for Versions { } } +impl From for Versions { + fn from(version: SemVersion) -> Versions { + Versions::SemVer(version) + } +} + +#[cfg(feature = "chocolatey")] +#[cfg_attr(docsrs, doc(cfg(feature = "chocolatey")))] +impl From for Versions { + fn from(version: chocolatey::ChocoVersion) -> Versions { + Versions::Choco(version) + } +} + #[cfg(test)] mod tests { use rstest::rstest;