Repository: AmbientRun/Ambient
File: crates/network/src/serialization.rs (Rust)

Code before:
/// A wrapper around a type that can be serialized and deserialized transparently but captures deserialize errors instead of failing.
/// This allows for more graceful handling of errors in the deserialization process and for getting partial results.
#[derive(Clone, Debug)]
pub enum FailableDeserialization<T> {
    Ok(T),
    Error(String),
}

impl<T> FailableDeserialization<T> {
    pub fn ok(self) -> Option<T> {
        match self {
            FailableDeserialization::Ok(t) => Some(t),
            FailableDeserialization::Error(_) => None,
        }
    }
}

impl<T> From<T> for FailableDeserialization<T> {
    fn from(t: T) -> Self {
        FailableDeserialization::Ok(t)
    }
}

impl<T> From<FailableDeserialization<T>> for Result<T, String> {
    fn from(value: FailableDeserialization<T>) -> Self {
        match value {
            FailableDeserialization::Ok(t) => Ok(t),
            FailableDeserialization::Error(e) => Err(e),
        }
    }
}

impl<T> serde::Serialize for FailableDeserialization<T>
where
    T: serde::Serialize,
{
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        match self {
            FailableDeserialization::Ok(t) => t.serialize(serializer),
            FailableDeserialization::Error(_) => Err(serde::ser::Error::custom(
                "Cannot serialize FailableDeserialization::Error",
            )),
        }
    }
}

impl<'de, T> serde::Deserialize<'de> for FailableDeserialization<T>
where
    T: serde::Deserialize<'de>,

Review comment (suggestion):
Should be https://doc.rust-lang.org/std/convert/trait.TryInto.html, or we can just use the newtype patterns and `.into_inner()`

Code after:
/// A wrapper around a type that can be serialized and deserialized transparently but captures deserialize errors
/// instead of failing.
/// This allows for more graceful handling of errors in the deserialization process and for getting partial results.
///
/// Note that when using FailableDeserialization for part of your type then it is supposed to be used for last field(s)
/// of your type as anything deserialized after it will most likely fail.
#[derive(Clone, Debug)]
pub struct FailableDeserialization<T>(Result<T, String>);

impl<T> FailableDeserialization<T> {
    pub fn into_inner(self) -> Result<T, String> {
        self.0
    }
}

impl<T> From<T> for FailableDeserialization<T> {
    fn from(t: T) -> Self {
        Ok(t).into()
    }
}

impl<T> From<Result<T, String>> for FailableDeserialization<T> {
    fn from(result: Result<T, String>) -> Self {
        Self(result)
    }
}

impl<T> serde::Serialize for FailableDeserialization<T>
where
    T: serde::Serialize,
{
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        match &self.0 {
            Ok(t) => t.serialize(serializer),
            Err(_) => Err(serde::ser::Error::custom(
                "Cannot serialize FailableDeserialization::Error",
            )),
        }
    }
}

impl<'de, T> serde::Deserialize<'de> for FailableDeserialization<T>
where
    T: serde::Deserialize<'de>,
{
    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        Ok(T::deserialize(deserializer)
            .map_err(|e| e.to_string())
            .into())