diff --git a/saphyr/CHANGELOG.md b/saphyr/CHANGELOG.md index 1b55c7f..603b5d3 100644 --- a/saphyr/CHANGELOG.md +++ b/saphyr/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## Upcoming + +**Features** + +- ([#19](https://github.com/Ethiraric/yaml-rust2/pull/19)) `Yaml` now + implements `IndexMut` and `IndexMut<&'a str>`. These functions may not + return a mutable reference to a `BAD_VALUE`. Instead, `index_mut()` will + panic if either: + * The index is out of range, as per `IndexMut`'s requirements + * The inner `Yaml` variant doesn't match `Yaml::Array` for `usize` or + `Yaml::Hash` for `&'a str` + ## v0.8.0 **Breaking Changes**: diff --git a/saphyr/src/yaml.rs b/saphyr/src/yaml.rs index 6d58653..9486d1c 100644 --- a/saphyr/src/yaml.rs +++ b/saphyr/src/yaml.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use std::ops::ControlFlow; -use std::{collections::BTreeMap, convert::TryFrom, mem, ops::Index}; +use std::{collections::BTreeMap, convert::TryFrom, mem, ops::Index, ops::IndexMut}; use encoding_rs::{Decoder, DecoderResult, Encoding}; use hashlink::LinkedHashMap; @@ -478,6 +478,23 @@ pub fn $name(&self) -> Option<$t> { ); ); +macro_rules! define_as_mut_ref ( + ($name:ident, $t:ty, $yt:ident) => ( +/// Get a mutable reference to the inner object in the YAML enum if it is a `$t`. +/// +/// # Return +/// If the variant of `self` is `Yaml::$yt`, return `Some(&mut $t)` with the `$t` contained. +/// Otherwise, return `None`. +#[must_use] +pub fn $name(&mut self) -> Option<$t> { + match *self { + Yaml::$yt(ref mut v) => Some(v), + _ => None + } +} + ); +); + macro_rules! define_into ( ($name:ident, $t:ty, $yt:ident) => ( /// Get the inner object in the YAML enum if it is a `$t`. @@ -503,6 +520,9 @@ impl Yaml { define_as_ref!(as_hash, &Hash, Hash); define_as_ref!(as_vec, &Array, Array); + define_as_mut_ref!(as_mut_hash, &mut Hash, Hash); + define_as_mut_ref!(as_mut_vec, &mut Array, Array); + define_into!(into_bool, bool, Boolean); define_into!(into_i64, i64, Integer); define_into!(into_string, String, String); @@ -641,6 +661,16 @@ impl<'a> Index<&'a str> for Yaml { } } +impl<'a> IndexMut<&'a str> for Yaml { + fn index_mut(&mut self, idx: &'a str) -> &mut Yaml { + let key = Yaml::String(idx.to_owned()); + match self.as_mut_hash() { + Some(h) => h.get_mut(&key).unwrap(), + None => panic!("Not a hash type"), + } + } +} + impl Index for Yaml { type Output = Yaml; @@ -656,6 +686,28 @@ impl Index for Yaml { } } +impl IndexMut for Yaml { + /// Perform indexing if `self` is a sequence or a mapping. + /// + /// # Panics + /// This function panics if the index given is out of range (as per [`IndexMut`]). If `self` i + /// a [`Yaml::Array`], this is when the index is bigger or equal to the length of the + /// underlying `Vec`. If `self` is a [`Yaml::Hash`], this is when the mapping sequence does no + /// contain [`Yaml::Integer`]`(idx)` as a key. + /// + /// This function also panics if `self` is not a [`Yaml::Array`] nor a [`Yaml::Hash`]. + fn index_mut(&mut self, idx: usize) -> &mut Yaml { + match self { + Yaml::Array(sequence) => sequence.index_mut(idx), + Yaml::Hash(mapping) => { + let key = Yaml::Integer(i64::try_from(idx).unwrap()); + mapping.get_mut(&key).unwrap() + } + _ => panic!("Attempting to index but `self` is not a sequence nor a mapping"), + } + } +} + impl IntoIterator for Yaml { type Item = Yaml; type IntoIter = YamlIter;