parlov-core 0.5.0

Shared types, error types, and oracle class definitions for parlov.
Documentation
//! Private serde helpers for `http` and `bytes` types that lack native serde support.
//!
//! Each submodule is used via `#[serde(with = "...")]` on the fields that need it. They are not
//! part of the public API and exist solely to satisfy the serde derive machinery.

/// Serializes `StatusCode` as its `u16` representation.
pub(crate) mod status_code_serde {
    use http::StatusCode;
    use serde::{Deserialize, Deserializer, Serialize, Serializer};

    // serde `with` module protocol requires `&T` — cannot pass StatusCode by value here.
    #[allow(clippy::trivially_copy_pass_by_ref)]
    pub(crate) fn serialize<S: Serializer>(code: &StatusCode, s: S) -> Result<S::Ok, S::Error> {
        code.as_u16().serialize(s)
    }

    pub(crate) fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<StatusCode, D::Error> {
        let n = u16::deserialize(d)?;
        StatusCode::from_u16(n).map_err(serde::de::Error::custom)
    }
}

/// Serializes `Method` as its string representation (e.g. `"GET"`).
pub(crate) mod method_serde {
    use http::Method;
    use serde::{Deserialize, Deserializer, Serialize, Serializer};

    pub(crate) fn serialize<S: Serializer>(method: &Method, s: S) -> Result<S::Ok, S::Error> {
        method.as_str().serialize(s)
    }

    pub(crate) fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Method, D::Error> {
        let s = String::deserialize(d)?;
        s.parse::<Method>().map_err(serde::de::Error::custom)
    }
}

/// Serializes `HeaderMap` as a flat `{name: value}` JSON object.
///
/// Headers with non-UTF-8 values are silently dropped during serialization; they are not
/// expected in the probe/analysis pipeline.
pub(crate) mod header_map_serde {
    use http::HeaderMap;
    use serde::{Deserialize, Deserializer, Serialize, Serializer};
    use std::collections::HashMap;

    pub(crate) fn serialize<S: Serializer>(
        headers: &HeaderMap,
        s: S,
    ) -> Result<S::Ok, S::Error> {
        let map: HashMap<&str, &str> = headers
            .iter()
            .filter_map(|(k, v)| v.to_str().ok().map(|v_str| (k.as_str(), v_str)))
            .collect();
        map.serialize(s)
    }

    pub(crate) fn deserialize<'de, D: Deserializer<'de>>(
        d: D,
    ) -> Result<HeaderMap, D::Error> {
        let map = HashMap::<String, String>::deserialize(d)?;
        let mut headers = HeaderMap::new();
        for (k, v) in map {
            let name = k
                .parse::<http::header::HeaderName>()
                .map_err(serde::de::Error::custom)?;
            let value = v
                .parse::<http::header::HeaderValue>()
                .map_err(serde::de::Error::custom)?;
            headers.insert(name, value);
        }
        Ok(headers)
    }
}

/// Serializes `Bytes` as a byte sequence (`Vec<u8>`).
pub(crate) mod bytes_serde {
    use bytes::Bytes;
    use serde::{Deserialize, Deserializer, Serialize, Serializer};

    pub(crate) fn serialize<S: Serializer>(b: &Bytes, s: S) -> Result<S::Ok, S::Error> {
        b.as_ref().serialize(s)
    }

    pub(crate) fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Bytes, D::Error> {
        let v = Vec::<u8>::deserialize(d)?;
        Ok(Bytes::from(v))
    }
}

/// Serializes `Option<Bytes>` as an optional byte sequence.
pub(crate) mod opt_bytes_serde {
    use bytes::Bytes;
    use serde::{Deserialize, Deserializer, Serializer};

    // serde `with` module protocol requires `&Option<T>` — cannot use `Option<&T>` here.
    #[allow(clippy::ref_option)]
    pub(crate) fn serialize<S: Serializer>(
        opt: &Option<Bytes>,
        s: S,
    ) -> Result<S::Ok, S::Error> {
        match opt {
            Some(b) => s.serialize_some(b.as_ref()),
            None => s.serialize_none(),
        }
    }

    pub(crate) fn deserialize<'de, D: Deserializer<'de>>(
        d: D,
    ) -> Result<Option<Bytes>, D::Error> {
        let opt = Option::<Vec<u8>>::deserialize(d)?;
        Ok(opt.map(Bytes::from))
    }
}