cinema 0.1.0

HTTP record-replay proxy for Rust tests
Documentation
use crate::prelude::*;
use serde::ser::SerializeMap;
use serde::{de, ser};
use std::fmt;

#[derive(Default, Serialize, Deserialize)]
pub struct RecordReplayRecords {
    pub requests: Vec<RecordReplayRecord>,
}

#[derive(Serialize, Deserialize)]
pub struct RecordReplayRecord {
    #[serde(
        serialize_with = "serialize_method",
        deserialize_with = "deserialize_method"
    )]
    pub method: Method,
    #[serde(serialize_with = "serialize_uri", deserialize_with = "deserialize_uri")]
    pub uri: Uri,
    #[serde(
        serialize_with = "serialize_header_map",
        deserialize_with = "deserialize_header_map",
        skip_serializing_if = "Option::is_none",
        default
    )]
    pub headers: Option<HeaderMap<HeaderValue>>,
    #[serde(
        serialize_with = "serialize_option_bytes",
        deserialize_with = "deserialize_option_bytes",
        skip_serializing_if = "Option::is_none",
        default
    )]
    pub body: Option<Bytes>,
    pub response: RecordReplayRecordResponse,
}

#[derive(Serialize, Deserialize)]
pub struct RecordReplayRecordResponse {
    #[serde(
        serialize_with = "serialize_status_code",
        deserialize_with = "deserialize_status_code"
    )]
    pub status: StatusCode,
    #[serde(
        serialize_with = "serialize_header_map",
        deserialize_with = "deserialize_header_map",
        skip_serializing_if = "Option::is_none",
        default
    )]
    pub headers: Option<HeaderMap<HeaderValue>>,
    #[serde(
        serialize_with = "serialize_bytes",
        deserialize_with = "deserialize_bytes"
    )]
    pub body: Bytes,
}

fn serialize_method<S>(method: &Method, serializer: S) -> Result<S::Ok, S::Error>
where
    S: ser::Serializer,
{
    serializer.serialize_str(method.as_str())
}

fn deserialize_method<'de, D>(deserializer: D) -> Result<Method, D::Error>
where
    D: de::Deserializer<'de>,
{
    struct MethodVisitor;

    impl<'de> de::Visitor<'de> for MethodVisitor {
        type Value = Method;

        fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "a valid HTTP method")
        }

        fn visit_str<E>(self, v: &str) -> Result<Method, E>
        where
            E: de::Error,
        {
            Method::from_bytes(v.as_bytes()).map_err(E::custom)
        }
    }

    deserializer.deserialize_str(MethodVisitor)
}

fn serialize_uri<S>(uri: &Uri, serializer: S) -> Result<S::Ok, S::Error>
where
    S: ser::Serializer,
{
    serializer.serialize_str(uri.to_string().as_str())
}

fn deserialize_uri<'de, D>(deserializer: D) -> Result<Uri, D::Error>
where
    D: de::Deserializer<'de>,
{
    struct UriVisitor;

    impl<'de> de::Visitor<'de> for UriVisitor {
        type Value = Uri;

        fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "a valid HTTP URI")
        }

        fn visit_str<E>(self, v: &str) -> Result<Uri, E>
        where
            E: de::Error,
        {
            v.parse::<Uri>().map_err(E::custom)
        }
    }

    deserializer.deserialize_str(UriVisitor)
}

fn serialize_status_code<S>(code: &StatusCode, serializer: S) -> Result<S::Ok, S::Error>
where
    S: ser::Serializer,
{
    serializer.serialize_i64(code.as_u16() as i64)
}

fn deserialize_status_code<'de, D>(deserializer: D) -> Result<StatusCode, D::Error>
where
    D: de::Deserializer<'de>,
{
    struct StatusCodeVisitor;

    impl<'de> de::Visitor<'de> for StatusCodeVisitor {
        type Value = StatusCode;

        fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "a valid HTTP status code")
        }

        fn visit_i64<E>(self, v: i64) -> Result<StatusCode, E>
        where
            E: de::Error,
        {
            StatusCode::from_u16(v as u16).map_err(E::custom)
        }

        fn visit_u64<E>(self, v: u64) -> Result<StatusCode, E>
        where
            E: de::Error,
        {
            self.visit_i64(v as i64)
        }
    }

    deserializer.deserialize_i64(StatusCodeVisitor)
}

fn serialize_header_map<S>(
    headers: &Option<HeaderMap<HeaderValue>>,
    serializer: S,
) -> Result<S::Ok, S::Error>
where
    S: ser::Serializer,
{
    match headers {
        Some(headers) => {
            let mut map = HashMap::new();
            for (key, value) in headers.iter() {
                map.insert(
                    key.as_str().to_owned(),
                    value.to_str().unwrap_or("").to_owned(),
                );
            }
            serializer.serialize_map(Some(map.len()))?.end()
        }

        None => serializer.serialize_none(),
    }
}

fn deserialize_header_map<'de, D>(
    deserializer: D,
) -> Result<Option<HeaderMap<HeaderValue>>, D::Error>
where
    D: de::Deserializer<'de>,
{
    struct HeaderMapVisitor;

    impl<'de> de::Visitor<'de> for HeaderMapVisitor {
        type Value = Option<HeaderMap<HeaderValue>>;

        fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "a header map")
        }

        fn visit_map<V>(self, mut visitor: V) -> Result<Option<HeaderMap<HeaderValue>>, V::Error>
        where
            V: de::MapAccess<'de>,
        {
            let mut map = HeaderMap::new();
            while let Some((key, value)) = visitor.next_entry::<String, String>()? {
                // [TODO] Handle invalid header names and values
                map.insert(key.parse::<HeaderName>().unwrap(), value.parse().unwrap());
            }
            Ok(Some(map))
        }
    }

    deserializer.deserialize_map(HeaderMapVisitor)
}

fn serialize_bytes<S>(bytes: &Bytes, serializer: S) -> Result<S::Ok, S::Error>
where
    S: ser::Serializer,
{
    let string = String::from_utf8(bytes.to_vec())
        .map_err(|e| ser::Error::custom(format!("Invalid UTF-8: {}", e)))?;
    serializer.serialize_str(&string)
}

fn deserialize_bytes<'de, D>(deserializer: D) -> Result<Bytes, D::Error>
where
    D: de::Deserializer<'de>,
{
    struct BytesVisitor;

    impl<'de> de::Visitor<'de> for BytesVisitor {
        type Value = Bytes;

        fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "a body string")
        }

        fn visit_str<E>(self, v: &str) -> Result<Bytes, E>
        where
            E: de::Error,
        {
            Ok(Bytes::from(v.as_bytes().to_vec()))
        }
    }

    deserializer.deserialize_str(BytesVisitor)
}

fn serialize_option_bytes<S>(bytes: &Option<Bytes>, serializer: S) -> Result<S::Ok, S::Error>
where
    S: ser::Serializer,
{
    match bytes {
        Some(bytes) => serialize_bytes(bytes, serializer),
        None => serializer.serialize_none(),
    }
}

fn deserialize_option_bytes<'de, D>(deserializer: D) -> Result<Option<Bytes>, D::Error>
where
    D: de::Deserializer<'de>,
{
    struct OptionBytesVisitor;

    impl<'de> de::Visitor<'de> for OptionBytesVisitor {
        type Value = Option<Bytes>;

        fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "an optional body string")
        }

        fn visit_none<E>(self) -> Result<Option<Bytes>, E>
        where
            E: de::Error,
        {
            Ok(None)
        }

        fn visit_some<D>(self, deserializer: D) -> Result<Option<Bytes>, D::Error>
        where
            D: de::Deserializer<'de>,
        {
            let bytes = deserialize_bytes(deserializer)?;
            Ok(Some(bytes))
        }
    }

    deserializer.deserialize_option(OptionBytesVisitor)
}