From 2c0ad8a6c457a72e952f630c8993efb54c4d50cc Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 25 Jun 2021 17:23:31 +0200 Subject: Add conditional dependency variant This patch adds a conditional dependency variant to the build/runtime dependency deserialization type(s). Signed-off-by: Matthias Beyer --- src/package/dependency/build.rs | 9 +++-- src/package/dependency/condition.rs | 67 +++++++++++++++++++++++++++++++++++-- src/package/dependency/mod.rs | 2 +- src/package/dependency/runtime.rs | 9 +++-- 4 files changed, 79 insertions(+), 8 deletions(-) diff --git a/src/package/dependency/build.rs b/src/package/dependency/build.rs index 78d3fad..42c0763 100644 --- a/src/package/dependency/build.rs +++ b/src/package/dependency/build.rs @@ -12,22 +12,25 @@ use anyhow::Result; use serde::Deserialize; use serde::Serialize; -use crate::package::dependency::ParseDependency; -use crate::package::dependency::StringEqual; use crate::package::PackageName; use crate::package::PackageVersionConstraint; +use crate::package::dependency::ParseDependency; +use crate::package::dependency::StringEqual; +use crate::package::dependency::condition::Condition; /// A dependency that is packaged and is only required during build time #[derive(Serialize, Deserialize, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] #[serde(untagged)] pub enum BuildDependency { Simple(String), + Conditional(String, Condition), } impl AsRef for BuildDependency { fn as_ref(&self) -> &str { match self { BuildDependency::Simple(name) => name, + BuildDependency::Conditional(name, _) => name, } } } @@ -36,6 +39,7 @@ impl StringEqual for BuildDependency { fn str_equal(&self, s: &str) -> bool { match self { BuildDependency::Simple(name) => name == s, + BuildDependency::Conditional(name, _) => name == s, } } } @@ -61,6 +65,7 @@ mod tests { let s: TestSetting = toml::from_str(r#"setting = "foo""#).expect("Parsing TestSetting failed"); match s.setting { BuildDependency::Simple(name) => assert_eq!(name, "foo", "Expected 'foo', got {}", name), + other => panic!("Unexpected deserialization to other variant: {:?}", other), } } } diff --git a/src/package/dependency/condition.rs b/src/package/dependency/condition.rs index df65a5c..9de498e 100644 --- a/src/package/dependency/condition.rs +++ b/src/package/dependency/condition.rs @@ -11,6 +11,7 @@ use std::collections::HashMap; use serde::Deserialize; +use serde::Serialize; use crate::util::EnvironmentVariableName; @@ -23,7 +24,7 @@ use crate::util::EnvironmentVariableName; /// build image is used. /// All these settings are optional, of course. /// -#[derive(Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] pub struct Condition { #[serde(rename = "has_env", skip_serializing_if = "Option::is_none")] has_env: Option>, @@ -35,11 +36,71 @@ pub struct Condition { in_image: Option>, } +/// Manual implementation of PartialOrd for Condition +/// +/// Because HashMap does not implement PartialOrd +impl PartialOrd for Condition { + fn partial_cmp(&self, other: &Self) -> Option { + use std::cmp::Ordering as O; + + let cmp_has_env = match (self.has_env.as_ref(), other.has_env.as_ref()) { + (Some(a), Some(b)) => a.partial_cmp(b), + (Some(_), None) => Some(O::Greater), + (None, Some(_)) => Some(O::Less), + (None, None) => Some(O::Equal), + }; + + if cmp_has_env.as_ref().map(|o| *o != O::Equal).unwrap_or(false) { + return cmp_has_env + } + + let cmp_env_eq = match (self.env_eq.as_ref(), other.env_eq.as_ref()) { + // TODO: Is this safe? We ignore the HashMaps here and just say they are equal. They are most certainly not. + (Some(_), Some(_)) => Some(O::Equal), + (Some(_), None) => Some(O::Greater), + (None, Some(_)) => Some(O::Less), + (None, None) => Some(O::Equal), + }; + + if cmp_env_eq.as_ref().map(|o| *o != O::Equal).unwrap_or(false) { + return cmp_env_eq + } + + match (self.in_image.as_ref(), other.in_image.as_ref()) { + (Some(a), Some(b)) => a.partial_cmp(b), + (Some(_), None) => Some(O::Greater), + (None, Some(_)) => Some(O::Less), + (None, None) => Some(O::Equal), + } + } +} + +/// Manual implementation of Ord for Condition +/// +/// Because HashMap does not implement Ord +impl Ord for Condition { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.partial_cmp(other).unwrap_or(std::cmp::Ordering::Equal) + } +} + +/// Manual implementation of Hash for Condition +/// +/// Because HashMap does not implement Hash +impl std::hash::Hash for Condition { + fn hash(&self, state: &mut H) { + self.has_env.hash(state); + if let Some(hm) = self.env_eq.as_ref() { + hm.iter().for_each(|(k, v)| (k, v).hash(state)); + }; + self.in_image.hash(state); + } +} + /// Helper type for supporting Vec and T in value /// position of Condition -#[cfg_attr(test, derive(Eq, PartialEq))] -#[derive(Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] #[serde(untagged)] pub enum OneOrMore { One(T), diff --git a/src/package/dependency/mod.rs b/src/package/dependency/mod.rs index 8195dd1..e55dfc2 100644 --- a/src/package/dependency/mod.rs +++ b/src/package/dependency/mod.rs @@ -24,7 +24,7 @@ pub use build::*; mod runtime; pub use runtime::*; -mod condition; +pub(self) mod condition; pub trait StringEqual { fn str_equal(&self, s: &str) -> bool; diff --git a/src/package/dependency/runtime.rs b/src/package/dependency/runtime.rs index 3916b22..26679e2 100644 --- a/src/package/dependency/runtime.rs +++ b/src/package/dependency/runtime.rs @@ -12,22 +12,25 @@ use anyhow::Result; use serde::Deserialize; use serde::Serialize; -use crate::package::dependency::ParseDependency; -use crate::package::dependency::StringEqual; use crate::package::PackageName; use crate::package::PackageVersionConstraint; +use crate::package::dependency::ParseDependency; +use crate::package::dependency::StringEqual; +use crate::package::dependency::condition::Condition; /// A dependency that is packaged and is required during runtime #[derive(Serialize, Deserialize, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] #[serde(untagged)] pub enum Dependency { Simple(String), + Conditional(String, Condition), } impl AsRef for Dependency { fn as_ref(&self) -> &str { match self { Dependency::Simple(name) => name, + Dependency::Conditional(name, _) => name, } } } @@ -36,6 +39,7 @@ impl StringEqual for Dependency { fn str_equal(&self, s: &str) -> bool { match self { Dependency::Simple(name) => name == s, + Dependency::Conditional(name, _) => name == s, } } } @@ -68,6 +72,7 @@ mod tests { match s.setting { Dependency::Simple(name) => assert_eq!(name, "foo", "Expected 'foo', got {}", name), + other => panic!("Unexpected deserialization to other variant: {:?}", other), } } } -- cgit v1.2.3