From aa2d0b8d1d18acb00bddd4eb6b4e0ceedc6260d5 Mon Sep 17 00:00:00 2001 From: Paul Nettleton Date: Mon, 3 Jun 2024 05:43:34 -0500 Subject: [PATCH] feat: add `Options::apply_merge()` Merges `<<` keys into the surrounding mapping. Closes: #2 --- src/options.rs | 69 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 6 deletions(-) diff --git a/src/options.rs b/src/options.rs index 9c14f7d..f3a8548 100644 --- a/src/options.rs +++ b/src/options.rs @@ -8,18 +8,64 @@ use crate::{Compose, YamlValue}; #[allow(missing_copy_implementations)] // Will include interpolation vars as a HashMap. #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct Options { - /// TODO - merge_anchors: bool, + /// Whether to perform merging of `<<` keys. + apply_merge: bool, } impl Options { + /// Set whether to merge `<<` keys into the surrounding mapping. + /// + /// ``` + /// use compose_spec::Compose; + /// + /// let yaml = " + /// services: + /// one: + /// environment: &env + /// FOO: foo + /// BAR: bar + /// two: + /// environment: + /// <<: *env + /// BAR: baz + /// "; + /// + /// let compose = Compose::options() + /// .apply_merge(true) + /// .from_yaml_str(yaml) + /// .unwrap(); + /// + /// let two_env = compose.services["two"] + /// .environment + /// .clone() + /// .into_map() + /// .unwrap(); + /// + /// assert_eq!(two_env["FOO"].as_ref().unwrap().as_string().unwrap(), "foo"); + /// assert_eq!(two_env["BAR"].as_ref().unwrap().as_string().unwrap(), "baz"); + /// ``` + pub fn apply_merge(&mut self, apply_merge: bool) -> &mut Self { + self.apply_merge = apply_merge; + self + } + + /// Return `true` if any options are set. + const fn any(&self) -> bool { + let Self { apply_merge } = *self; + apply_merge + } + /// Use the set options to deserialize a [`Compose`] file from a string slice of YAML. /// /// # Errors /// /// Returns an error if deserialization fails. pub fn from_yaml_str(&self, yaml: &str) -> serde_yaml::Result { - serde_yaml::from_str(yaml) + if self.any() { + self.from_yaml_value(serde_yaml::from_str(yaml)?) + } else { + serde_yaml::from_str(yaml) + } } /// Use the set options to deserialize a [`Compose`] file from an IO stream of YAML. @@ -28,7 +74,11 @@ impl Options { /// /// Returns an error if deserialization fails. pub fn from_yaml_reader(&self, reader: R) -> serde_yaml::Result { - serde_yaml::from_reader(reader) + if self.any() { + self.from_yaml_value(serde_yaml::from_reader(reader)?) + } else { + serde_yaml::from_reader(reader) + } } /// Use the set options to deserialize a [`Compose`] file from bytes of YAML. @@ -37,7 +87,11 @@ impl Options { /// /// Returns an error if deserialization fails. pub fn from_yaml_slice(&self, slice: &[u8]) -> serde_yaml::Result { - serde_yaml::from_slice(slice) + if self.any() { + self.from_yaml_value(serde_yaml::from_slice(slice)?) + } else { + serde_yaml::from_slice(slice) + } } /// Use the set options to deserialize a [`Compose`] file from a YAML [`Value`](YamlValue). @@ -45,7 +99,10 @@ impl Options { /// # Errors /// /// Returns an error if deserialization fails. - pub fn from_yaml_value(&self, value: YamlValue) -> serde_yaml::Result { + pub fn from_yaml_value(&self, mut value: YamlValue) -> serde_yaml::Result { + if self.apply_merge { + value.apply_merge()?; + } serde_yaml::from_value(value) } }