use super::Value;
use crate::prelude::*;
use core::cmp::Ordering;
use core::hash::{Hash, Hasher};
use serde::{Deserialize, Serialize};
#[must_use]
pub fn nobang(s: &str) -> &str {
s.strip_prefix('!').unwrap_or(s)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MaybeTag<T> {
Tag(String),
NotTag(T),
}
pub fn check_for_tag<T: fmt::Display>(value: &T) -> MaybeTag<String> {
let s = value.to_string();
if s.starts_with('!') {
MaybeTag::Tag(s)
} else {
MaybeTag::NotTag(s)
}
}
pub(crate) const TAGGED_VALUE_FIELD_TAG: &str = "$__noyalib_tag";
pub(crate) const TAGGED_VALUE_FIELD_VALUE: &str = "$__noyalib_value";
#[derive(Debug, Clone)]
pub struct Tag(String);
impl Tag {
#[must_use]
pub fn new(tag: impl Into<String>) -> Self {
Self(tag.into())
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub fn into_string(self) -> String {
self.0
}
#[must_use]
pub fn nobang(&self) -> &str {
nobang(&self.0)
}
}
impl fmt::Display for Tag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<&str> for Tag {
fn from(s: &str) -> Self {
Self::new(s)
}
}
impl From<String> for Tag {
fn from(s: String) -> Self {
Self(s)
}
}
impl AsRef<str> for Tag {
fn as_ref(&self) -> &str {
&self.0
}
}
impl PartialEq for Tag {
fn eq(&self, other: &Self) -> bool {
nobang(&self.0) == nobang(&other.0)
}
}
impl Eq for Tag {}
impl Hash for Tag {
fn hash<H: Hasher>(&self, state: &mut H) {
nobang(&self.0).hash(state);
}
}
impl PartialOrd for Tag {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Tag {
fn cmp(&self, other: &Self) -> Ordering {
nobang(&self.0).cmp(nobang(&other.0))
}
}
impl TryFrom<&[u8]> for Tag {
type Error = core::str::Utf8Error;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
core::str::from_utf8(bytes).map(Tag::new)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TaggedValue {
tag: Tag,
pub(crate) value: Box<Value>,
}
impl TaggedValue {
#[must_use]
pub fn new(tag: Tag, value: Value) -> Self {
Self {
tag,
value: Box::new(value),
}
}
#[must_use]
pub fn tag(&self) -> &Tag {
&self.tag
}
#[must_use]
pub fn value(&self) -> &Value {
&self.value
}
#[must_use]
pub fn value_mut(&mut self) -> &mut Value {
&mut self.value
}
#[must_use]
pub fn into_parts(self) -> (Tag, Value) {
(self.tag, *self.value)
}
}
impl fmt::Display for TaggedValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {}", self.tag, self.value)
}
}
impl Serialize for TaggedValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeMap;
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry(self.tag.as_str(), self.value())?;
map.end()
}
}
impl<'de> Deserialize<'de> for TaggedValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::{MapAccess, Visitor};
struct TaggedValueVisitor;
impl<'de> Visitor<'de> for TaggedValueVisitor {
type Value = TaggedValue;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a single-entry map representing a tagged value")
}
fn visit_map<A>(self, mut map: A) -> Result<TaggedValue, A::Error>
where
A: MapAccess<'de>,
{
let (tag, value): (String, Value) = map
.next_entry()?
.ok_or_else(|| serde::de::Error::custom("expected a single-entry map"))?;
Ok(TaggedValue::new(Tag::new(tag), value))
}
}
deserializer.deserialize_map(TaggedValueVisitor)
}
}
impl<'de> serde::Deserializer<'de> for &'de TaggedValue {
type Error = crate::Error;
fn deserialize_any<V>(self, visitor: V) -> crate::Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
visitor.visit_map(TaggedValueMapAccess {
tag: Some(self.tag.as_str()),
value: Some(self.value()),
})
}
fn deserialize_enum<V>(
self,
_name: &'static str,
_variants: &'static [&'static str],
visitor: V,
) -> crate::Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
visitor.visit_enum(TaggedValueEnumAccess { tagged: self })
}
serde::forward_to_deserialize_any! {
bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes
byte_buf option unit unit_struct newtype_struct seq tuple
tuple_struct map struct identifier ignored_any
}
}
struct TaggedValueMapAccess<'de> {
tag: Option<&'de str>,
value: Option<&'de Value>,
}
impl<'de> serde::de::MapAccess<'de> for TaggedValueMapAccess<'de> {
type Error = crate::Error;
fn next_key_seed<K>(&mut self, seed: K) -> crate::Result<Option<K::Value>>
where
K: serde::de::DeserializeSeed<'de>,
{
match self.tag.take() {
Some(tag) => seed
.deserialize(serde::de::value::BorrowedStrDeserializer::new(tag))
.map(Some),
None => Ok(None),
}
}
fn next_value_seed<V>(&mut self, seed: V) -> crate::Result<V::Value>
where
V: serde::de::DeserializeSeed<'de>,
{
match self.value.take() {
Some(value) => seed.deserialize(value),
None => Err(serde::de::Error::custom("value is missing")),
}
}
}
pub(crate) struct TagPreservingMapAccess<'de> {
state: TagPreservingState<'de>,
}
#[derive(Clone, Copy)]
enum TagPreservingState<'de> {
EmitTagKey { tag: &'de str, value: &'de Value },
EmitTagValue { tag: &'de str, value: &'de Value },
EmitValueKey { value: &'de Value },
EmitValueValue { value: &'de Value },
Done,
}
impl<'de> TagPreservingMapAccess<'de> {
pub(crate) fn new(tag: &'de str, value: &'de Value) -> Self {
Self {
state: TagPreservingState::EmitTagKey { tag, value },
}
}
}
impl<'de> serde::de::MapAccess<'de> for TagPreservingMapAccess<'de> {
type Error = crate::Error;
fn next_key_seed<K>(&mut self, seed: K) -> crate::Result<Option<K::Value>>
where
K: serde::de::DeserializeSeed<'de>,
{
match self.state {
TagPreservingState::EmitTagKey { tag, value } => {
self.state = TagPreservingState::EmitTagValue { tag, value };
seed.deserialize(
serde::de::value::BorrowedStrDeserializer::<crate::Error>::new(
TAGGED_VALUE_FIELD_TAG,
),
)
.map(Some)
}
TagPreservingState::EmitValueKey { value } => {
self.state = TagPreservingState::EmitValueValue { value };
seed.deserialize(
serde::de::value::BorrowedStrDeserializer::<crate::Error>::new(
TAGGED_VALUE_FIELD_VALUE,
),
)
.map(Some)
}
TagPreservingState::Done => Ok(None),
TagPreservingState::EmitTagValue { .. } | TagPreservingState::EmitValueValue { .. } => {
Err(serde::de::Error::custom(
"TagPreservingMapAccess: next_key called before next_value",
))
}
}
}
fn next_value_seed<V>(&mut self, seed: V) -> crate::Result<V::Value>
where
V: serde::de::DeserializeSeed<'de>,
{
match self.state {
TagPreservingState::EmitTagValue { tag, value } => {
self.state = TagPreservingState::EmitValueKey { value };
seed.deserialize(
serde::de::value::BorrowedStrDeserializer::<crate::Error>::new(tag),
)
}
TagPreservingState::EmitValueValue { value } => {
self.state = TagPreservingState::Done;
seed.deserialize(crate::de::Deserializer::with_options_preserving_tags(
value, None, false,
))
}
_ => Err(serde::de::Error::custom(
"TagPreservingMapAccess: next_value called out of order",
)),
}
}
}
struct TaggedValueEnumAccess<'de> {
tagged: &'de TaggedValue,
}
impl<'de> serde::de::EnumAccess<'de> for TaggedValueEnumAccess<'de> {
type Error = crate::Error;
type Variant = TaggedValueVariantAccess<'de>;
fn variant_seed<V>(self, seed: V) -> crate::Result<(V::Value, Self::Variant)>
where
V: serde::de::DeserializeSeed<'de>,
{
let variant = seed.deserialize(
serde::de::value::BorrowedStrDeserializer::<crate::Error>::new(
self.tagged.tag.nobang(),
),
)?;
Ok((
variant,
TaggedValueVariantAccess {
value: self.tagged.value(),
},
))
}
}
struct TaggedValueVariantAccess<'de> {
value: &'de Value,
}
impl<'de> serde::de::VariantAccess<'de> for TaggedValueVariantAccess<'de> {
type Error = crate::Error;
fn unit_variant(self) -> crate::Result<()> {
Ok(())
}
fn newtype_variant_seed<T>(self, seed: T) -> crate::Result<T::Value>
where
T: serde::de::DeserializeSeed<'de>,
{
seed.deserialize(self.value)
}
fn tuple_variant<V>(self, _len: usize, visitor: V) -> crate::Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
serde::Deserializer::deserialize_seq(self.value, visitor)
}
fn struct_variant<V>(
self,
_fields: &'static [&'static str],
visitor: V,
) -> crate::Result<V::Value>
where
V: serde::de::Visitor<'de>,
{
serde::Deserializer::deserialize_map(self.value, visitor)
}
}