use crate::{parser::Anchors, repr::*, *};
use alloc::string::{String, ToString};
use core::{
fmt::{Debug, Formatter},
hash::{Hash, Hasher},
iter::FromIterator,
marker::PhantomData,
ops::Index,
};
macro_rules! as_method {
{$($(#[$meta:meta])* fn $id:ident = $ty:ident$(($op:ident))?
$(| ($default:expr)?)?
$(| $ty2:ident)* -> $r:ty)+} => {$(
$(#[$meta])*
pub fn $id(&self) -> Result<$r, u64> {
match self.yaml() {
Yaml::$ty(v) $(| Yaml::$ty2(v))* => Ok(v$(.$op())?),
$(Yaml::Null => Ok($default),)?
_ => Err(self.pos),
}
}
)+};
}
macro_rules! impl_iter {
($(impl $item:ty)+) => {
$(impl<R: Repr> FromIterator<$item> for Node<R> {
fn from_iter<T: IntoIterator<Item = $item>>(iter: T) -> Self {
Self::from(iter.into_iter().collect::<Yaml<R>>())
}
})+
};
}
pub type NodeRc = Node<RcRepr>;
pub type NodeArc = Node<ArcRepr>;
pub struct Node<R: Repr> {
pos: u64,
tag: String,
yaml: R::Rc,
_marker: PhantomData<R>,
}
impl<R: Repr> Node<R> {
pub fn new(yaml: impl Into<Yaml<R>>, pos: u64, tag: impl ToString) -> Self {
Self::new_repr(R::new_rc(yaml.into()), pos, tag)
}
pub fn new_repr(yaml: R::Rc, pos: u64, tag: impl ToString) -> Self {
Self {
yaml,
pos,
tag: tag.to_string(),
_marker: PhantomData,
}
}
pub fn set_yaml(&mut self, yaml: impl Into<Yaml<R>>) {
self.set_repr(R::new_rc(yaml.into()));
}
pub fn set_repr(&mut self, yaml: R::Rc) {
self.yaml = yaml;
}
pub fn pos(&self) -> u64 {
self.pos
}
pub fn tag(&self) -> &str {
match self.tag.as_str() {
"" => match self.yaml() {
Yaml::Null => concat!(parser::tag_prefix!(), "null"),
Yaml::Bool(_) => concat!(parser::tag_prefix!(), "bool"),
Yaml::Int(_) => concat!(parser::tag_prefix!(), "int"),
Yaml::Float(_) => concat!(parser::tag_prefix!(), "float"),
Yaml::Str(_) => concat!(parser::tag_prefix!(), "str"),
Yaml::Seq(_) => concat!(parser::tag_prefix!(), "seq"),
Yaml::Map(_) => concat!(parser::tag_prefix!(), "map"),
Yaml::Alias(_) => "",
},
s => s,
}
}
pub fn yaml(&self) -> &Yaml<R> {
&self.yaml
}
pub fn clone_yaml(&self) -> R::Rc {
self.yaml.clone()
}
pub fn rc_ref(&self) -> &R::Rc {
&self.yaml
}
pub fn is_null(&self) -> bool {
*self.yaml() == Yaml::Null
}
pub fn as_int(&self) -> Result<i64, u64> {
match self.yaml() {
Yaml::Int(s) => to_i64(s).map_err(|_| self.pos),
_ => Err(self.pos),
}
}
pub fn as_float(&self) -> Result<f64, u64> {
match self.yaml() {
Yaml::Float(s) => to_f64(s).map_err(|_| self.pos),
_ => Err(self.pos),
}
}
pub fn as_number(&self) -> Result<f64, u64> {
match self.yaml() {
Yaml::Int(s) => to_i64(s).map(|n| n as f64).map_err(|_| self.pos),
Yaml::Float(s) => to_f64(s).map_err(|_| self.pos),
_ => Err(self.pos),
}
}
as_method! {
fn as_bool = Bool(clone) -> bool
fn as_str = Str | ("")? -> &str
fn as_seq = Seq(clone) -> Seq<R>
fn as_map = Map(clone) -> Map<R>
}
pub fn as_value(&self) -> Result<&str, u64> {
match self.yaml() {
Yaml::Str(s) | Yaml::Int(s) | Yaml::Float(s) => Ok(s),
Yaml::Bool(true) => Ok("true"),
Yaml::Bool(false) => Ok("false"),
Yaml::Null => Ok(""),
_ => Err(self.pos),
}
}
pub fn as_anchor<'a>(&'a self, anchors: &'a Anchors<R>) -> Result<&'a Self, u64> {
if let Yaml::Alias(a) = self.yaml() {
anchors.get(a).ok_or(self.pos)
} else {
Ok(self)
}
}
pub fn get<Y: Into<Self>>(&self, key: Y) -> Result<&Self, u64> {
if let Yaml::Map(m) = self.yaml() {
m.get(&key.into()).ok_or(self.pos)
} else {
Err(self.pos)
}
}
pub fn get_default<'a, Y, Ret, F>(
&'a self,
key: Y,
default: Ret,
factory: F,
) -> Result<Ret, u64>
where
Y: Into<Self>,
F: FnOnce(&'a Self) -> Result<Ret, u64>,
{
if let Yaml::Map(m) = self.yaml() {
if let Some(n) = m.get(&key.into()) {
factory(n)
} else {
Ok(default)
}
} else {
Err(self.pos)
}
}
pub fn get_ind(&self, ind: Ind) -> Result<&Self, u64> {
if let Yaml::Seq(v) = self.yaml() {
v.get(ind.0).ok_or(self.pos)
} else {
Err(self.pos)
}
}
}
impl<R: Repr> Debug for Node<R> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "Node{:?}", &self.yaml)
}
}
impl<R: Repr> Clone for Node<R> {
fn clone(&self) -> Self {
Self {
tag: self.tag.clone(),
yaml: self.clone_yaml(),
..*self
}
}
}
impl<R: Repr> Hash for Node<R> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.yaml.hash(state)
}
}
impl<R: Repr> PartialEq for Node<R> {
fn eq(&self, rhs: &Self) -> bool {
self.yaml.eq(&rhs.yaml)
}
}
impl<R: Repr> Eq for Node<R> {}
pub struct Ind(pub usize);
impl<R: Repr> Index<Ind> for Node<R> {
type Output = Self;
fn index(&self, index: Ind) -> &Self::Output {
if let Yaml::Seq(v) = self.yaml() {
v.index(index.0)
} else {
panic!("out of bound!")
}
}
}
impl<R, I> Index<I> for Node<R>
where
R: Repr,
I: Into<Self>,
{
type Output = Self;
fn index(&self, index: I) -> &Self::Output {
if let Yaml::Map(m) = self.yaml() {
m.get(&index.into())
.unwrap_or_else(|| panic!("out of bound!"))
} else {
panic!("out of bound!")
}
}
}
impl<R, Y> From<Y> for Node<R>
where
R: Repr,
Y: Into<Yaml<R>>,
{
fn from(yaml: Y) -> Self {
Self::new(yaml, 0, "")
}
}
impl_iter! {
impl Self
impl (Self, Self)
}