#![allow(clippy::module_name_repetitions)]
use std::{
borrow::Cow,
hash::{BuildHasher, Hasher},
ops::{Index, IndexMut},
};
use hashlink::LinkedHashMap;
use saphyr_parser::{ScalarStyle, Tag};
use crate::{LoadableYamlNode, ScalarOwned, Yaml};
#[derive(Clone, PartialEq, PartialOrd, Debug, Eq, Ord, Hash)]
pub enum YamlOwned {
Representation(String, ScalarStyle, Option<Tag>),
Value(ScalarOwned),
Sequence(SequenceOwned),
Mapping(MappingOwned),
Tagged(Tag, Box<YamlOwned>),
Alias(usize),
BadValue,
}
pub type SequenceOwned = Vec<YamlOwned>;
pub type MappingOwned = LinkedHashMap<YamlOwned, YamlOwned>;
define_yaml_object_impl!(
YamlOwned,
mappingtype = MappingOwned,
sequencetype = SequenceOwned,
nodetype = Self,
scalartype = { ScalarOwned },
selfname = "YAMLOwned",
owned
);
impl YamlOwned {
#[must_use]
fn as_mapping_get_impl(&self, key: &str) -> Option<&Self> {
match self.as_mapping() {
Some(mapping) => {
let hash = hash_str_as_yaml_string(key, mapping.hasher().build_hasher());
mapping
.raw_entry()
.from_hash(hash, |k| k.as_str() == Some(key))
.map(|(_, v)| v)
}
_ => None,
}
}
#[must_use]
fn as_mapping_get_mut_impl(&mut self, key: &str) -> Option<&mut Self> {
use hashlink::linked_hash_map::RawEntryMut::{Occupied, Vacant};
match self.as_mapping_mut() {
Some(mapping) => {
let hash = hash_str_as_yaml_string(key, mapping.hasher().build_hasher());
match mapping
.raw_entry_mut()
.from_hash(hash, |k| k.as_str() == Some(key))
{
Occupied(entry) => Some(entry.into_mut()),
Vacant(_) => None,
}
}
_ => None,
}
}
}
impl LoadableYamlNode<'_> for YamlOwned {
type HashKey = Self;
fn from_bare_yaml(yaml: Yaml<'_>) -> Self {
match yaml {
Yaml::Sequence(_) => Self::Sequence(vec![]),
Yaml::Mapping(_) => Self::Mapping(MappingOwned::new()),
Yaml::Representation(cow, scalar_style, tag) => {
Self::Representation(cow.into(), scalar_style, tag.map(Cow::into_owned))
}
Yaml::Value(scalar) => Self::Value(scalar.into_owned()),
Yaml::Tagged(tag, mut node) => Self::Tagged(
tag.into_owned(),
Box::new(Self::from_bare_yaml(node.take())),
),
Yaml::Alias(x) => Self::Alias(x),
Yaml::BadValue => Self::BadValue,
}
}
fn is_sequence(&self) -> bool {
self.is_sequence() || self.get_tagged_node().map_or(false, YamlOwned::is_sequence)
}
fn is_mapping(&self) -> bool {
self.is_mapping() || self.get_tagged_node().map_or(false, YamlOwned::is_mapping)
}
fn is_badvalue(&self) -> bool {
self.is_badvalue()
}
fn into_tagged(self, tag: Cow<'_, Tag>) -> Self {
Self::Tagged(tag.into_owned(), Box::new(self))
}
fn sequence_mut(&mut self) -> &mut Vec<Self> {
if self.is_sequence() {
self.as_vec_mut().unwrap()
} else if let Some(vec) = self.get_tagged_node_mut().and_then(YamlOwned::as_vec_mut) {
vec
} else {
panic!("Called sequence_mut on a non-array")
}
}
fn mapping_mut(&mut self) -> &mut LinkedHashMap<Self::HashKey, Self> {
if self.is_mapping() {
self.as_mapping_mut().unwrap()
} else if let Some(map) = self
.get_tagged_node_mut()
.and_then(YamlOwned::as_mapping_mut)
{
map
} else {
panic!("Called mapping_mut on a non-array")
}
}
fn take(&mut self) -> Self {
let mut taken_out = Self::BadValue;
std::mem::swap(&mut taken_out, self);
taken_out
}
}
impl IntoIterator for YamlOwned {
type Item = Self;
type IntoIter = YamlOwnedIter;
fn into_iter(self) -> Self::IntoIter {
YamlOwnedIter {
yaml: self.into_vec().unwrap_or_default().into_iter(),
}
}
}
pub struct YamlOwnedIter {
yaml: std::vec::IntoIter<YamlOwned>,
}
impl Iterator for YamlOwnedIter {
type Item = YamlOwned;
fn next(&mut self) -> Option<YamlOwned> {
self.yaml.next()
}
}
fn hash_str_as_yaml_string<H: Hasher>(key: &str, mut hasher: H) -> u64 {
use std::hash::Hash;
let key = YamlOwned::Value(ScalarOwned::String(key.into()));
key.hash(&mut hasher);
hasher.finish()
}