use core::mem;
use alloc::vec::Vec;
use crate::yaml::data::{Data, Id, StringId};
use crate::yaml::raw::{self, new_bool, new_string, Raw};
use crate::yaml::{Block, Mapping, Separator, ValueMut};
pub struct MappingMut<'a> {
data: &'a mut Data,
pub(crate) id: Id,
}
macro_rules! insert_float {
($name:ident, $ty:ty, $string:literal, $lit:literal, $hint:ident) => {
#[doc = concat!("Set the value as a ", $string, ".")]
#[doc = concat!("value.", stringify!($name), "(\"number2\", ", stringify!($lit), ");")]
#[doc = concat!(" number2: ", stringify!($lit))]
pub fn $name<K>(&mut self, key: K, value: $ty)
where
K: AsRef<[u8]>,
{
let mut buffer = ryu::Buffer::new();
let number = self.data.insert_str(buffer.format(value));
let value = Raw::Number(raw::Number::new(number, crate::yaml::serde_hint::$hint));
self.inner_insert(key.as_ref(), Separator::Auto, value);
}
};
}
macro_rules! insert_number {
($name:ident, $ty:ty, $string:literal, $lit:literal, $hint:ident) => {
#[doc = concat!("Set the value as a ", $string, ".")]
#[doc = concat!("value.", stringify!($name), "(\"number2\", ", stringify!($lit), ");")]
#[doc = concat!(" number2: ", stringify!($lit))]
pub fn $name<K>(&mut self, key: K, value: $ty)
where
K: AsRef<[u8]>,
{
let mut buffer = itoa::Buffer::new();
let number = self.data.insert_str(buffer.format(value));
let value = Raw::Number(raw::Number::new(number, crate::yaml::serde_hint::$hint));
self.inner_insert(key.as_ref(), Separator::Auto, value);
}
};
}
impl<'a> MappingMut<'a> {
pub(crate) fn new(data: &'a mut Data, id: Id) -> Self {
Self { data, id }
}
fn make_prefix(&mut self) -> StringId {
let mut out = Vec::new();
out.push(raw::NEWLINE);
out.resize(
self.data.mapping(self.id).indent.saturating_add(1),
raw::SPACE,
);
self.data.insert_str(out)
}
fn inner_insert(&mut self, key: &[u8], separator: Separator<'_>, value: Raw) -> Id {
let key = self.data.insert_str(key);
if let Some(id) = self
.data
.mapping(self.id)
.items
.iter()
.map(|id| self.data.mapping_item(*id))
.find(|item| item.key.id == key)
.map(|item| item.value)
{
self.data.replace(id, value);
return id;
}
let key = raw::String::new(raw::RawStringKind::Bare, key, key);
let item_prefix = if self.data.mapping(self.id).items.last().is_some() {
self.make_prefix()
} else {
self.data.insert_str("")
};
let item_id = self
.data
.insert(Raw::Null(raw::Null::Empty), item_prefix, Some(self.id));
let value_prefix = match separator {
Separator::Auto => {
if value.is_tabular() {
let mapping = self.data.mapping(self.id);
if let Some(last) = mapping.items.last() {
self.data.layout(self.data.mapping_item(*last).value).prefix
} else {
let mut value_prefix = Vec::new();
value_prefix.push(raw::NEWLINE);
value_prefix.resize(mapping.indent.saturating_add(2), raw::SPACE);
self.data.insert_str(&value_prefix)
}
} else {
self.data.insert_str(" ")
}
}
Separator::Custom(separator) => self.data.insert_str(separator),
};
let value = self.data.insert(value, value_prefix, Some(item_id));
self.data
.replace(item_id, Raw::MappingItem(raw::MappingItem { key, value }));
self.data.mapping_mut(self.id).items.push(item_id);
value
}
#[must_use]
#[inline]
pub fn as_ref(&self) -> Mapping<'_> {
Mapping::new(self.data, self.id)
}
#[must_use]
#[inline]
pub fn into_ref(self) -> Mapping<'a> {
Mapping::new(self.data, self.id)
}
pub fn get_mut(&mut self, key: &str) -> Option<ValueMut<'_>> {
for item in &self.data.mapping(self.id).items {
let item = self.data.mapping_item(*item);
if self.data.str(item.key.id) == key {
return Some(ValueMut::new(self.data, item.value));
}
}
None
}
#[must_use]
pub fn get_into_mut(self, key: &str) -> Option<ValueMut<'a>> {
for item in &self.data.mapping(self.id).items {
let item = self.data.mapping_item(*item);
if self.data.str(item.key.id) == key {
return Some(ValueMut::new(self.data, item.value));
}
}
None
}
pub fn remove(&mut self, key: &str) -> bool {
let mut index = None;
for (i, item) in self.data.mapping(self.id).items.iter().enumerate() {
let item = self.data.mapping_item(*item);
if self.data.str(item.key.id) == key {
index = Some(i);
break;
}
}
let Some(index) = index else {
return false;
};
let item = self.data.mapping_mut(self.id).items.remove(index);
self.data.drop(item);
true
}
pub fn clear(&mut self) {
let mut items = mem::take(&mut self.data.mapping_mut(self.id).items);
for item in items.drain(..) {
self.data.drop(item);
}
self.data.mapping_mut(self.id).items = items;
}
pub fn insert<K>(&mut self, key: K, separator: Separator<'_>) -> ValueMut<'_>
where
K: AsRef<[u8]>,
{
let value = self.inner_insert(key.as_ref(), separator, Raw::Null(raw::Null::Empty));
ValueMut::new(self.data, value)
}
pub fn insert_str<K, S>(&mut self, key: K, string: S)
where
K: AsRef<[u8]>,
S: AsRef<str>,
{
let string = new_string(self.data, string);
self.inner_insert(key.as_ref(), Separator::Auto, string);
}
pub fn insert_block<K, I>(&mut self, key: K, iter: I, block: Block)
where
K: AsRef<[u8]>,
I: IntoIterator,
I::Item: AsRef<str>,
{
let value = raw::new_block(self.data, self.id, iter, block);
self.inner_insert(key.as_ref(), Separator::Auto, value);
}
pub fn insert_bool<K>(&mut self, key: K, value: bool)
where
K: AsRef<[u8]>,
{
let value = new_bool(self.data, value);
self.inner_insert(key.as_ref(), Separator::Auto, value);
}
insert_float!(insert_f32, f32, "32-bit float", 10.42, F32);
insert_float!(insert_f64, f64, "64-bit float", 10.42, F64);
insert_number!(insert_u8, u8, "8-bit unsigned integer", 42, U8);
insert_number!(insert_i8, i8, "8-bit signed integer", -42, I8);
insert_number!(insert_u16, u16, "16-bit unsigned integer", 42, U16);
insert_number!(insert_i16, i16, "16-bit signed integer", -42, I16);
insert_number!(insert_u32, u32, "32-bit unsigned integer", 42, U32);
insert_number!(insert_i32, i32, "32-bit signed integer", -42, I32);
insert_number!(insert_u64, u64, "64-bit unsigned integer", 42, U64);
insert_number!(insert_i64, i64, "64-bit signed integer", -42, I64);
insert_number!(insert_u128, u128, "128-bit unsigned integer", 42, U128);
insert_number!(insert_i128, i128, "128-bit signed integer", -42, I128);
}