#![allow(clippy::module_name_repetitions)]
use std::{
borrow::Cow,
convert::TryFrom,
hash::{BuildHasher, Hasher},
ops::{Index, IndexMut},
};
use hashlink::LinkedHashMap;
use saphyr_parser::{ScalarStyle, Tag};
use crate::{LoadableYamlNode, Scalar, YamlOwned};
#[derive(Clone, PartialEq, PartialOrd, Debug, Eq, Ord, Hash)]
pub enum Yaml<'input> {
Representation(Cow<'input, str>, ScalarStyle, Option<Cow<'input, Tag>>),
Value(Scalar<'input>),
Sequence(Sequence<'input>),
Mapping(Mapping<'input>),
Tagged(Cow<'input, Tag>, Box<Yaml<'input>>),
Alias(usize),
BadValue,
}
pub type Sequence<'input> = Vec<Yaml<'input>>;
pub type Mapping<'input> = LinkedHashMap<Yaml<'input>, Yaml<'input>>;
define_yaml_object_impl!(
Yaml<'input>,
<'input>,
mappingtype = Mapping<'input>,
sequencetype = Sequence<'input>,
nodetype = Self,
scalartype = { Scalar },
selfname = "YAML",
borrowing
);
impl Yaml<'_> {
#[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<'input> LoadableYamlNode<'input> for Yaml<'input> {
type HashKey = Self;
fn from_bare_yaml(yaml: Yaml<'input>) -> Self {
yaml
}
fn is_sequence(&self) -> bool {
self.is_sequence()
}
fn is_mapping(&self) -> bool {
self.is_mapping()
}
fn is_badvalue(&self) -> bool {
self.is_badvalue()
}
fn sequence_mut(&mut self) -> &mut Vec<Self> {
self.as_vec_mut()
.expect("Called sequence_mut on a non-array")
}
fn mapping_mut(&mut self) -> &mut LinkedHashMap<Self::HashKey, Self> {
self.as_mapping_mut()
.expect("Called mapping_mut on a non-hash")
}
fn into_tagged(self, tag: Cow<'input, Tag>) -> Self {
Self::Tagged(tag, Box::new(self))
}
fn take(&mut self) -> Self {
let mut taken_out = Yaml::BadValue;
std::mem::swap(&mut taken_out, self);
taken_out
}
}
impl<'input> IntoIterator for Yaml<'input> {
type Item = Yaml<'input>;
type IntoIter = YamlIter<'input>;
fn into_iter(self) -> Self::IntoIter {
YamlIter {
yaml: self.into_vec().unwrap_or_default().into_iter(),
}
}
}
pub struct YamlIter<'input> {
yaml: std::vec::IntoIter<Yaml<'input>>,
}
impl<'input> Iterator for YamlIter<'input> {
type Item = Yaml<'input>;
fn next(&mut self) -> Option<Yaml<'input>> {
self.yaml.next()
}
}
fn hash_str_as_yaml_string<H: Hasher>(key: &str, mut hasher: H) -> u64 {
use std::hash::Hash;
let key = Yaml::Value(Scalar::String(key.into()));
key.hash(&mut hasher);
hasher.finish()
}
impl<'input> From<&'input YamlOwned> for Yaml<'input> {
fn from(value: &'input YamlOwned) -> Self {
match value {
YamlOwned::Representation(str, scalar_style, tag) => Yaml::Representation(
Cow::Borrowed(str),
*scalar_style,
tag.as_ref().map(Cow::Borrowed),
),
YamlOwned::Value(scalar_owned) => Yaml::Value(scalar_owned.into()),
YamlOwned::Sequence(yaml_owneds) => Yaml::Sequence(
yaml_owneds
.iter()
.map(Into::into)
.collect::<Vec<Yaml<'input>>>(),
),
YamlOwned::Mapping(linked_hash_map) => Yaml::Mapping(
linked_hash_map
.iter()
.map(|(key, value)| (key.into(), value.into()))
.collect::<Mapping>(),
),
YamlOwned::Tagged(tag, node) => {
Yaml::Tagged(Cow::Borrowed(tag), Box::new(node.as_ref().into()))
}
YamlOwned::Alias(usize) => Yaml::Alias(*usize),
YamlOwned::BadValue => Yaml::BadValue,
}
}
}