yaml: Implement IndexMut

This implements the IndexMut trait for Yaml. This allows indexing the
Yaml type while having a mutable reference.

Unlike the Index, this will panic on a failure. That is allowed as per
the Rust documentation [1]. We don't have the option of returning a
mutable reference to BAD_VALUE as that is unsafe. So instead we just
panic.

1: https://doc.rust-lang.org/std/ops/trait.IndexMut.html#tymethod.index_mut

Resolves: https://github.com/chyh1990/yaml-rust/issues/123
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Co-authored-by: Ethiraric <ethiraric@gmail.com>
This commit is contained in:
Alistair Francis 2024-03-30 18:51:16 +01:00 committed by Ethiraric
parent 2642252f7f
commit 0f1dedc584
2 changed files with 65 additions and 1 deletions

View file

@ -1,5 +1,17 @@
# Changelog # Changelog
## Upcoming
**Features**
- ([#19](https://github.com/Ethiraric/yaml-rust2/pull/19)) `Yaml` now
implements `IndexMut<usize>` 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 ## v0.8.0
**Breaking Changes**: **Breaking Changes**:

View file

@ -4,7 +4,7 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::ops::ControlFlow; 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 encoding_rs::{Decoder, DecoderResult, Encoding};
use hashlink::LinkedHashMap; 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 ( macro_rules! define_into (
($name:ident, $t:ty, $yt:ident) => ( ($name:ident, $t:ty, $yt:ident) => (
/// Get the inner object in the YAML enum if it is a `$t`. /// 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_hash, &Hash, Hash);
define_as_ref!(as_vec, &Array, Array); 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_bool, bool, Boolean);
define_into!(into_i64, i64, Integer); define_into!(into_i64, i64, Integer);
define_into!(into_string, String, String); 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<usize> for Yaml { impl Index<usize> for Yaml {
type Output = Yaml; type Output = Yaml;
@ -656,6 +686,28 @@ impl Index<usize> for Yaml {
} }
} }
impl IndexMut<usize> 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 { impl IntoIterator for Yaml {
type Item = Yaml; type Item = Yaml;
type IntoIter = YamlIter; type IntoIter = YamlIter;