use core::fmt;
use bstr::{BStr, ByteSlice};
#[cfg(feature = "serde-edits")]
use serde::{Deserialize, Serialize};
use crate::yaml::data::{Data, Id};
use crate::yaml::raw::Raw;
use crate::yaml::{Any, Mapping, Number, Sequence, String};
#[derive(Default, Debug, Clone, Copy)]
#[non_exhaustive]
pub enum Chomp {
Strip,
#[default]
Clip,
Keep,
}
impl Chomp {
pub(crate) fn as_byte(self) -> Option<u8> {
match self {
Chomp::Strip => Some(b'-'),
Chomp::Clip => None,
Chomp::Keep => Some(b'+'),
}
}
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum StringKind {
Bare,
Single,
Double,
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum Block {
Literal(Chomp),
Folded(Chomp),
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum Separator<'a> {
Auto,
Custom(&'a str),
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde-edits", derive(Serialize, Deserialize))]
#[non_exhaustive]
pub enum Null {
Keyword,
Tilde,
Empty,
}
pub struct Value<'a> {
pub(crate) data: &'a Data,
pub(crate) id: Id,
}
macro_rules! as_number {
($name:ident, $ty:ty, $doc:literal, $lit:literal) => {
#[doc = concat!("Try and get the value as a ", $doc, ".")]
#[doc = concat!("let doc = yaml::from_slice(\"", stringify!($lit), "\")?;")]
#[doc = concat!("let value = doc.as_ref().", stringify!($name), "();")]
#[doc = concat!("assert_eq!(value, Some(", stringify!($lit), "));")]
#[must_use]
pub fn $name(&self) -> Option<$ty> {
match self.data.raw(self.id) {
Raw::Number(raw) => {
let string = self.data.str(raw.string);
lexical_core::parse(string).ok()
}
_ => None,
}
}
};
}
impl<'a> Value<'a> {
pub(crate) fn new(data: &'a Data, id: Id) -> Self {
Self { data, id }
}
#[must_use]
pub fn into_any(self) -> Any<'a> {
match self.data.raw(self.id) {
Raw::Null(..) => Any::Null,
Raw::Boolean(bool) => Any::Bool(bool.value),
Raw::Number(number) => Any::Number(Number::new(self.data, number)),
Raw::String(string) => Any::String(String::new(self.data, string)),
Raw::Mapping(..) => Any::Mapping(Mapping::new(self.data, self.id)),
Raw::Sequence(..) => Any::Sequence(Sequence::new(self.data, self.id)),
_ => Any::Raw(self),
}
}
#[must_use]
pub fn as_any(&self) -> Any<'_> {
match self.data.raw(self.id) {
Raw::Null(..) => Any::Null,
Raw::Boolean(bool) => Any::Bool(bool.value),
Raw::Number(number) => Any::Number(Number::new(self.data, number)),
Raw::String(string) => Any::String(String::new(self.data, string)),
Raw::Mapping(..) => Any::Mapping(Mapping::new(self.data, self.id)),
Raw::Sequence(..) => Any::Sequence(Sequence::new(self.data, self.id)),
_ => Any::Raw(Value::new(self.data, self.id)),
}
}
#[must_use]
#[inline]
pub fn id(&self) -> Id {
self.id
}
#[must_use]
pub fn as_bstr(&self) -> Option<&'a BStr> {
match self.data.raw(self.id) {
Raw::String(raw) => Some(self.data.str(raw.id)),
_ => None,
}
}
#[must_use]
pub fn as_str(&self) -> Option<&'a str> {
match self.data.raw(self.id) {
Raw::String(raw) => self.data.str(raw.id).to_str().ok(),
_ => None,
}
}
#[must_use]
pub fn as_bool(&self) -> Option<bool> {
match self.data.raw(self.id) {
Raw::Boolean(bool) => Some(bool.value),
_ => None,
}
}
#[must_use]
pub fn as_mapping(&self) -> Option<Mapping<'a>> {
match self.data.raw(self.id) {
Raw::Mapping(..) => Some(Mapping::new(self.data, self.id)),
_ => None,
}
}
#[must_use]
pub fn as_sequence(&self) -> Option<Sequence<'a>> {
match self.data.raw(self.id) {
Raw::Sequence(..) => Some(Sequence::new(self.data, self.id)),
_ => None,
}
}
#[must_use]
pub fn as_number(&self) -> Option<Number<'_>> {
match self.data.raw(self.id) {
Raw::Number(raw) => Some(Number::new(self.data, raw)),
_ => None,
}
}
#[must_use]
pub fn into_number(self) -> Option<Number<'a>> {
match self.data.raw(self.id) {
Raw::Number(raw) => Some(Number::new(self.data, raw)),
_ => None,
}
}
as_number!(as_f32, f32, "32-bit float", 10.42);
as_number!(as_f64, f64, "64-bit float", 10.42);
as_number!(as_u8, u8, "8-bit unsigned integer", 42);
as_number!(as_i8, i8, "8-bit signed integer", -42);
as_number!(as_u16, u16, "16-bit unsigned integer", 42);
as_number!(as_i16, i16, "16-bit signed integer", -42);
as_number!(as_u32, u32, "16-bit unsigned integer", 42);
as_number!(as_i32, i32, "32-bit signed integer", -42);
as_number!(as_u64, u64, "16-bit unsigned integer", 42);
as_number!(as_i64, i64, "64-bit signed integer", -42);
as_number!(as_u128, u128, "16-bit unsigned integer", 42);
as_number!(as_i128, i128, "128-bit signed integer", -42);
}
impl fmt::Display for Value<'_> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.data.raw(self.id).display(self.data, f, None)
}
}
impl fmt::Debug for Value<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct Display<'a, 'b>(&'a Value<'b>);
impl fmt::Debug for Display<'_, '_> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0
.data
.raw(self.0.id)
.display(self.0.data, f, Some(self.0.id))
}
}
f.debug_tuple("Value").field(&Display(self)).finish()
}
}