use std::{
borrow::Cow,
hash::{BuildHasher, Hasher},
marker::PhantomData,
ops::{Index, IndexMut},
};
use hashlink::LinkedHashMap;
use saphyr_parser::{ScalarStyle, Tag};
use crate::{annotated::AnnotatedNode, Scalar};
#[derive(Clone, PartialEq, PartialOrd, Debug, Eq, Ord, Hash)]
pub enum YamlData<'input, Node>
where
Node: std::hash::Hash + std::cmp::Eq + From<Self> + AnnotatedNode,
{
Representation(Cow<'input, str>, ScalarStyle, Option<Cow<'input, Tag>>),
Value(Scalar<'input>),
Sequence(AnnotatedSequence<Node>),
Mapping(AnnotatedMapping<'input, Node>),
Tagged(Cow<'input, Tag>, Box<Node>),
Alias(usize),
BadValue,
}
define_yaml_object_impl!(
YamlData<'input, Node>,
< 'input, Node>,
where {
Node: std::hash::Hash
+ std::cmp::Eq
+ From<Self>
+ AnnotatedNode
+ for<'a> std::cmp::PartialEq<Node::HashKey<'a>>,
},
mappingtype = AnnotatedMapping<'input, Node>,
sequencetype = AnnotatedSequence<Node>,
nodetype = Node,
scalartype = { Scalar },
selfname = "YamlData",
borrowing
);
impl<'input, Node> YamlData<'input, Node>
where
Node: std::hash::Hash
+ std::cmp::Eq
+ From<Self>
+ AnnotatedNode
+ for<'a> std::cmp::PartialEq<Node::HashKey<'a>>,
{
#[must_use]
pub fn take(&mut self) -> Self {
let mut taken_out = Self::BadValue;
std::mem::swap(self, &mut taken_out);
taken_out
}
fn as_mapping_get_impl<'a>(&self, key: &'a str) -> Option<&Node>
where
'input: 'a,
{
use std::hash::Hash;
match self {
Self::Mapping(mapping) => {
let needle = Node::HashKey::<'a>::from(YamlData::Value(Scalar::String(key.into())));
let mut hasher = mapping.hasher().build_hasher();
needle.hash(&mut hasher);
let hash = hasher.finish();
mapping
.raw_entry()
.from_hash(hash, |candidate| *candidate == needle)
.map(|(_, v)| v)
}
_ => None,
}
}
#[must_use]
fn as_mapping_get_mut_impl(&mut self, key: &str) -> Option<&mut Node> {
match self.as_mapping_mut() {
Some(mapping) => {
use hashlink::linked_hash_map::RawEntryMut::{Occupied, Vacant};
use std::hash::Hash;
let needle = Node::HashKey::<'_>::from(YamlData::Value(Scalar::String(key.into())));
let mut hasher = mapping.hasher().build_hasher();
needle.hash(&mut hasher);
let hash = hasher.finish();
match mapping
.raw_entry_mut()
.from_hash(hash, |candidate| *candidate == needle)
{
Occupied(entry) => Some(entry.into_mut()),
Vacant(_) => None,
}
}
_ => None,
}
}
}
impl<'input, Node> IntoIterator for YamlData<'input, Node>
where
Node: std::hash::Hash
+ std::cmp::Eq
+ From<Self>
+ AnnotatedNode
+ for<'a> std::cmp::PartialEq<Node::HashKey<'a>>,
{
type Item = Node;
type IntoIter = AnnotatedYamlIter<'input, Node>;
fn into_iter(self) -> Self::IntoIter {
Self::IntoIter {
yaml: self.into_vec().unwrap_or_default().into_iter(),
marker: PhantomData,
}
}
}
#[allow(clippy::module_name_repetitions)]
pub struct AnnotatedYamlIter<'input, Node>
where
Node: std::hash::Hash + std::cmp::Eq + From<YamlData<'input, Node>> + AnnotatedNode,
{
yaml: std::vec::IntoIter<Node>,
marker: PhantomData<&'input ()>,
}
impl<'input, Node> Iterator for AnnotatedYamlIter<'input, Node>
where
Node: std::hash::Hash + std::cmp::Eq + From<YamlData<'input, Node>> + AnnotatedNode,
{
type Item = Node;
fn next(&mut self) -> Option<Node> {
self.yaml.next()
}
}
#[allow(clippy::module_name_repetitions)]
pub type AnnotatedSequence<Node> = Vec<Node>;
#[allow(clippy::module_name_repetitions)]
pub type AnnotatedMapping<'input, Node> = LinkedHashMap<Node, Node>;