serde-saphyr 2.0.0

YAML (de)serializer for Serde, emphasizing panic-free parsing, fast builds and good error reporting. Supports properties, include, tag and comment capture
Documentation
use serde_core::ser::{self, Serialize, SerializeTupleStruct, Serializer};
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::{Arc, Mutex};

#[cfg(feature = "comments")]
use crate::Commented;
use crate::{
    ArcAnchor, ArcRecursion, ArcRecursive, ArcWeakAnchor, DoubleQuoted, FlowMap, FlowSeq,
    NullableTilde, RcAnchor, RcRecursion, RcRecursive, RcWeakAnchor, SingleQuoted, SpaceAfter,
    Tagged,
};

#[cfg(feature = "comments")]
use super::NAME_TUPLE_COMMENTED;
use super::{
    NAME_DOUBLE_QUOTED, NAME_FLOW_MAP, NAME_FLOW_SEQ, NAME_NULLABLE_TILDE, NAME_SINGLE_QUOTED,
    NAME_SPACE_AFTER, NAME_TUPLE_ANCHOR, NAME_TUPLE_TAGGED, NAME_TUPLE_WEAK,
};

// ------------------------------------------------------------
// Internal wrappers -> shape the stream Serde produces so our
// serializer can intercept them in a single pass.
// ------------------------------------------------------------

// Recursive weak payloads: defer locking/borrowing until actual value serialization.
struct RcRecursivePayload<'a, T>(&'a Rc<RefCell<Option<T>>>);
struct ArcRecursivePayload<'a, T>(&'a Arc<Mutex<Option<T>>>);

impl<T: Serialize> Serialize for RcRecursivePayload<'_, T> {
    fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
        let borrowed = self.0.borrow();
        if let Some(value) = borrowed.as_ref() {
            value.serialize(s)
        } else {
            s.serialize_unit()
        }
    }
}

impl<T: Serialize> Serialize for ArcRecursivePayload<'_, T> {
    fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
        let guard = self
            .0
            .lock()
            .map_err(|_| ser::Error::custom("recursive Arc anchor mutex poisoned"))?;
        if let Some(value) = guard.as_ref() {
            value.serialize(s)
        } else {
            s.serialize_unit()
        }
    }
}

// Top-level newtype wrappers for strong/weak simply wrap the real payloads.
impl<T: Serialize> Serialize for RcAnchor<T> {
    fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
        // delegate to tuple-struct the serializer knows how to intercept
        let mut ts = s.serialize_tuple_struct(NAME_TUPLE_ANCHOR, 2)?;
        let ptr = Rc::as_ptr(&self.0) as usize;
        ts.serialize_field(&ptr)?;
        ts.serialize_field(&*self.0)?;
        ts.end()
    }
}
impl<T: Serialize> Serialize for ArcAnchor<T> {
    fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
        let mut ts = s.serialize_tuple_struct(NAME_TUPLE_ANCHOR, 2)?;
        let ptr = Arc::as_ptr(&self.0) as usize;
        ts.serialize_field(&ptr)?;
        ts.serialize_field(&*self.0)?;
        ts.end()
    }
}
impl<T: Serialize> Serialize for RcRecursive<T> {
    fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
        let mut ts = s.serialize_tuple_struct(NAME_TUPLE_ANCHOR, 2)?;
        let ptr = Rc::as_ptr(&self.0) as usize;
        ts.serialize_field(&ptr)?;
        let borrowed = self.0.borrow();
        let value = borrowed
            .as_ref()
            .ok_or_else(|| ser::Error::custom("recursive Rc anchor not initialized"))?;
        ts.serialize_field(value)?;
        ts.end()
    }
}
impl<T: Serialize> Serialize for ArcRecursive<T> {
    fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
        let mut ts = s.serialize_tuple_struct(NAME_TUPLE_ANCHOR, 2)?;
        let ptr = Arc::as_ptr(&self.0) as usize;
        ts.serialize_field(&ptr)?;
        let guard = self
            .0
            .lock()
            .map_err(|_| ser::Error::custom("recursive Arc anchor mutex poisoned"))?;
        let value = guard
            .as_ref()
            .ok_or_else(|| ser::Error::custom("recursive Arc anchor not initialized"))?;
        ts.serialize_field(value)?;
        ts.end()
    }
}
impl<T: Serialize> Serialize for RcWeakAnchor<T> {
    fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
        let up = self.0.upgrade();
        let mut ts = s.serialize_tuple_struct(NAME_TUPLE_WEAK, 3)?;
        let ptr = self.0.as_ptr() as usize;
        ts.serialize_field(&ptr)?;
        ts.serialize_field(&up.is_some())?;
        if let Some(rc) = up {
            ts.serialize_field(&*rc)?;
        } else {
            ts.serialize_field(&())?; // ignored by our serializer
        }
        ts.end()
    }
}
impl<T: Serialize> Serialize for ArcWeakAnchor<T> {
    fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
        let up = self.0.upgrade();
        let mut ts = s.serialize_tuple_struct(NAME_TUPLE_WEAK, 3)?;
        let ptr = self.0.as_ptr() as usize;
        ts.serialize_field(&ptr)?;
        ts.serialize_field(&up.is_some())?;
        if let Some(arc) = up {
            ts.serialize_field(&*arc)?;
        } else {
            ts.serialize_field(&())?;
        }
        ts.end()
    }
}
impl<T: Serialize> Serialize for RcRecursion<T> {
    fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
        let up = self.0.upgrade();
        let mut ts = s.serialize_tuple_struct(NAME_TUPLE_WEAK, 3)?;
        let ptr = self.0.as_ptr() as usize;
        ts.serialize_field(&ptr)?;
        ts.serialize_field(&up.is_some())?;
        if let Some(rc) = up {
            ts.serialize_field(&RcRecursivePayload(&rc))?;
        } else {
            ts.serialize_field(&())?;
        }
        ts.end()
    }
}
impl<T: Serialize> Serialize for ArcRecursion<T> {
    fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
        let up = self.0.upgrade();
        let mut ts = s.serialize_tuple_struct(NAME_TUPLE_WEAK, 3)?;
        let ptr = self.0.as_ptr() as usize;
        ts.serialize_field(&ptr)?;
        ts.serialize_field(&up.is_some())?;
        if let Some(arc) = up {
            ts.serialize_field(&ArcRecursivePayload(&arc))?;
        } else {
            ts.serialize_field(&())?;
        }
        ts.end()
    }
}

// Hints for flow / block strings.
impl<T: Serialize> Serialize for FlowSeq<T> {
    fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
        s.serialize_newtype_struct(NAME_FLOW_SEQ, &self.0)
    }
}
impl<T: Serialize> Serialize for FlowMap<T> {
    fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
        s.serialize_newtype_struct(NAME_FLOW_MAP, &self.0)
    }
}
impl<T: Serialize> Serialize for SpaceAfter<T> {
    fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
        s.serialize_newtype_struct(NAME_SPACE_AFTER, &self.0)
    }
}
impl<T: AsRef<str>> Serialize for DoubleQuoted<T> {
    fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
        s.serialize_newtype_struct(NAME_DOUBLE_QUOTED, self.0.as_ref())
    }
}
impl<T: AsRef<str>> Serialize for SingleQuoted<T> {
    fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
        s.serialize_newtype_struct(NAME_SINGLE_QUOTED, self.0.as_ref())
    }
}
impl<T: Serialize> Serialize for NullableTilde<T> {
    fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
        if !s.is_human_readable() {
            return self.0.serialize(s);
        }

        match &self.0 {
            Some(value) => s.serialize_some(value),
            None => s.serialize_newtype_struct(NAME_NULLABLE_TILDE, &self.0),
        }
    }
}

#[cfg(feature = "comments")]
impl<T: Serialize> Serialize for Commented<T> {
    fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
        // Represent as a special tuple-struct with two fields: (comment, value)
        // so the serializer can stage the comment before serializing the value.
        let mut ts = s.serialize_tuple_struct(NAME_TUPLE_COMMENTED, 2)?;
        ts.serialize_field(&self.1)?; // comment first
        ts.serialize_field(&self.0)?; // then value
        ts.end()
    }
}

impl<T: Serialize> Serialize for Tagged<T> {
    fn serialize<S: Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
        let Some(tag) = self.1.as_ref() else {
            // No tag requested: place no requirement on the node at all, so the
            // inner value serializes exactly as it would unwrapped.
            return self.0.serialize(s);
        };

        // Stage the resolved tag before the value reveals whether it is a scalar
        // or collection, just as Commented stages its presentation metadata.
        let mut ts = s.serialize_tuple_struct(NAME_TUPLE_TAGGED, 2)?;
        ts.serialize_field(tag)?;
        ts.serialize_field(&self.0)?;
        ts.end()
    }
}