steamroom 0.2.0

Steam client protocol library for depots, manifests, and CDN
Documentation
use serde::de;
use serde::de::DeserializeSeed;
use serde::de::MapAccess;
use serde::de::Visitor;

use serde::Deserializer;
use std::fmt;

use super::key_value::KeyValue;
use super::key_value::KvValue;

#[derive(Debug)]
pub enum KvDeError {
    Custom(String),
    TypeMismatch {
        expected: &'static str,
        got: &'static str,
    },
}

impl fmt::Display for KvDeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Custom(msg) => write!(f, "{msg}"),
            Self::TypeMismatch { expected, got } => {
                write!(f, "expected {expected}, got {got}")
            }
        }
    }
}

impl std::error::Error for KvDeError {}

impl de::Error for KvDeError {
    fn custom<T: fmt::Display>(msg: T) -> Self {
        Self::Custom(msg.to_string())
    }
}

pub fn from_kv<'de, T: serde::Deserialize<'de>>(kv: &'de KeyValue) -> Result<T, KvDeError> {
    T::deserialize(KvValueDeserializer(&kv.value))
}

pub fn from_value<'de, T: serde::Deserialize<'de>>(value: &'de KvValue) -> Result<T, KvDeError> {
    T::deserialize(KvValueDeserializer(value))
}

struct KvValueDeserializer<'a>(&'a KvValue);

impl<'de> Deserializer<'de> for KvValueDeserializer<'de> {
    type Error = KvDeError;

    fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        match self.0 {
            KvValue::String(s) => visitor.visit_borrowed_str(s),
            KvValue::Int32(v) => visitor.visit_i32(*v),
            KvValue::Float32(v) => visitor.visit_f32(*v),
            KvValue::UInt64(v) => visitor.visit_u64(*v),
            KvValue::Int64(v) => visitor.visit_i64(*v),
            KvValue::Color(v) => visitor.visit_i32(*v),
            KvValue::Pointer(v) => visitor.visit_i32(*v),
            KvValue::Children(_) => self.deserialize_map(visitor),
        }
    }

    fn deserialize_bool<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        match self.0 {
            KvValue::String(s) => match s.as_str() {
                "1" | "true" => visitor.visit_bool(true),
                "0" | "false" => visitor.visit_bool(false),
                _ => Err(KvDeError::Custom(format!("cannot parse '{s}' as bool"))),
            },
            KvValue::Int32(v) => visitor.visit_bool(*v != 0),
            _ => Err(KvDeError::TypeMismatch {
                expected: "bool",
                got: kv_type_name(self.0),
            }),
        }
    }

    fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        match self.0 {
            KvValue::String(s) => visitor.visit_u32(
                s.parse()
                    .map_err(|_| KvDeError::Custom(format!("cannot parse '{s}' as u32")))?,
            ),
            KvValue::Int32(v) => visitor.visit_u32(*v as u32),
            KvValue::UInt64(v) => visitor.visit_u32(*v as u32),
            _ => Err(KvDeError::TypeMismatch {
                expected: "u32",
                got: kv_type_name(self.0),
            }),
        }
    }

    fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        match self.0 {
            KvValue::String(s) => visitor.visit_u64(
                s.parse()
                    .map_err(|_| KvDeError::Custom(format!("cannot parse '{s}' as u64")))?,
            ),
            KvValue::UInt64(v) => visitor.visit_u64(*v),
            KvValue::Int64(v) => visitor.visit_u64(*v as u64),
            KvValue::Int32(v) => visitor.visit_u64(*v as u64),
            _ => Err(KvDeError::TypeMismatch {
                expected: "u64",
                got: kv_type_name(self.0),
            }),
        }
    }

    fn deserialize_i32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        match self.0 {
            KvValue::String(s) => visitor.visit_i32(
                s.parse()
                    .map_err(|_| KvDeError::Custom(format!("cannot parse '{s}' as i32")))?,
            ),
            KvValue::Int32(v) => visitor.visit_i32(*v),
            _ => Err(KvDeError::TypeMismatch {
                expected: "i32",
                got: kv_type_name(self.0),
            }),
        }
    }

    fn deserialize_i64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        match self.0 {
            KvValue::String(s) => visitor.visit_i64(
                s.parse()
                    .map_err(|_| KvDeError::Custom(format!("cannot parse '{s}' as i64")))?,
            ),
            KvValue::Int64(v) => visitor.visit_i64(*v),
            KvValue::Int32(v) => visitor.visit_i64(*v as i64),
            _ => Err(KvDeError::TypeMismatch {
                expected: "i64",
                got: kv_type_name(self.0),
            }),
        }
    }

    fn deserialize_f32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        match self.0 {
            KvValue::Float32(v) => visitor.visit_f32(*v),
            KvValue::String(s) => visitor.visit_f32(
                s.parse()
                    .map_err(|_| KvDeError::Custom(format!("cannot parse '{s}' as f32")))?,
            ),
            _ => Err(KvDeError::TypeMismatch {
                expected: "f32",
                got: kv_type_name(self.0),
            }),
        }
    }

    fn deserialize_f64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        match self.0 {
            KvValue::Float32(v) => visitor.visit_f64(*v as f64),
            KvValue::String(s) => visitor.visit_f64(
                s.parse()
                    .map_err(|_| KvDeError::Custom(format!("cannot parse '{s}' as f64")))?,
            ),
            _ => Err(KvDeError::TypeMismatch {
                expected: "f64",
                got: kv_type_name(self.0),
            }),
        }
    }

    fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        match self.0 {
            KvValue::String(s) => visitor.visit_borrowed_str(s),
            _ => Err(KvDeError::TypeMismatch {
                expected: "string",
                got: kv_type_name(self.0),
            }),
        }
    }

    fn deserialize_string<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        self.deserialize_str(visitor)
    }

    fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        visitor.visit_some(self)
    }

    fn deserialize_map<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        match self.0 {
            KvValue::Children(map) => visitor.visit_map(KvMapAccess {
                iter: map.iter(),
                value: None,
            }),
            _ => Err(KvDeError::TypeMismatch {
                expected: "map/struct",
                got: kv_type_name(self.0),
            }),
        }
    }

    fn deserialize_struct<V: Visitor<'de>>(
        self,
        _name: &'static str,
        _fields: &'static [&'static str],
        visitor: V,
    ) -> Result<V::Value, Self::Error> {
        self.deserialize_map(visitor)
    }

    fn deserialize_ignored_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        visitor.visit_unit()
    }

    serde::forward_to_deserialize_any! {
        i8 i16 u8 u16 i128 u128 char bytes byte_buf unit unit_struct
        newtype_struct seq tuple tuple_struct enum identifier
    }
}

struct KvMapAccess<'a> {
    iter: std::collections::btree_map::Iter<'a, String, KeyValue>,
    value: Option<&'a KvValue>,
}

impl<'de> MapAccess<'de> for KvMapAccess<'de> {
    type Error = KvDeError;

    fn next_key_seed<K: DeserializeSeed<'de>>(
        &mut self,
        seed: K,
    ) -> Result<Option<K::Value>, Self::Error> {
        match self.iter.next() {
            Some((key, kv)) => {
                self.value = Some(&kv.value);
                seed.deserialize(de::value::BorrowedStrDeserializer::new(key))
                    .map(Some)
            }
            None => Ok(None),
        }
    }

    fn next_value_seed<V: DeserializeSeed<'de>>(
        &mut self,
        seed: V,
    ) -> Result<V::Value, Self::Error> {
        let value = self
            .value
            .take()
            .ok_or_else(|| KvDeError::Custom("value without key".into()))?;
        seed.deserialize(KvValueDeserializer(value))
    }
}

fn kv_type_name(v: &KvValue) -> &'static str {
    match v {
        KvValue::String(_) => "String",
        KvValue::Int32(_) => "Int32",
        KvValue::Float32(_) => "Float32",
        KvValue::UInt64(_) => "UInt64",
        KvValue::Int64(_) => "Int64",
        KvValue::Color(_) => "Color",
        KvValue::Pointer(_) => "Pointer",
        KvValue::Children(_) => "Children",
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::types::key_value::parse_text_kv;

    #[test]
    fn deserialize_flat_struct() {
        #[derive(serde::Deserialize, Debug, PartialEq)]
        struct AppState {
            appid: String,
            name: String,
            #[serde(rename = "Universe")]
            universe: String,
        }

        let input = r#""AppState"
{
    "appid"     "480"
    "name"      "Spacewar"
    "Universe"  "1"
}"#;
        let kv = parse_text_kv(input).unwrap();
        let app: AppState = from_kv(&kv).unwrap();
        assert_eq!(app.appid, "480");
        assert_eq!(app.name, "Spacewar");
        assert_eq!(app.universe, "1");
    }

    #[test]
    fn deserialize_numeric_from_string() {
        #[derive(serde::Deserialize, Debug)]
        struct Info {
            appid: u32,
            buildid: u64,
        }

        let input = r#""info"
{
    "appid"     "480"
    "buildid"   "3538192"
}"#;
        let kv = parse_text_kv(input).unwrap();
        let info: Info = from_kv(&kv).unwrap();
        assert_eq!(info.appid, 480);
        assert_eq!(info.buildid, 3538192);
    }

    #[test]
    fn deserialize_nested_struct() {
        #[derive(serde::Deserialize, Debug)]
        struct Root {
            depots: Depots,
        }

        #[derive(serde::Deserialize, Debug)]
        struct Depots {
            #[serde(rename = "481")]
            depot_481: Depot,
        }

        #[derive(serde::Deserialize, Debug)]
        struct Depot {
            manifests: Manifests,
        }

        #[derive(serde::Deserialize, Debug)]
        struct Manifests {
            public: Branch,
        }

        #[derive(serde::Deserialize, Debug)]
        struct Branch {
            gid: String,
        }

        let input = r#""root"
{
    "depots"
    {
        "481"
        {
            "manifests"
            {
                "public"
                {
                    "gid"   "3183503801510301321"
                }
            }
        }
    }
}"#;
        let kv = parse_text_kv(input).unwrap();
        let root: Root = from_kv(&kv).unwrap();
        assert_eq!(
            root.depots.depot_481.manifests.public.gid,
            "3183503801510301321"
        );
    }

    #[test]
    fn deserialize_optional_fields() {
        #[derive(serde::Deserialize, Debug)]
        #[allow(dead_code)]
        struct Branch {
            buildid: String,
            #[serde(default)]
            description: Option<String>,
            #[serde(default)]
            pwdrequired: Option<String>,
        }

        let input = r#""branch"
{
    "buildid"   "3538192"
}"#;
        let kv = parse_text_kv(input).unwrap();
        let branch: Branch = from_kv(&kv).unwrap();
        assert_eq!(branch.buildid, "3538192");
        assert!(branch.description.is_none());
    }

    #[test]
    fn deserialize_bool_from_string() {
        #[derive(serde::Deserialize, Debug)]
        struct Config {
            pwdrequired: bool,
        }

        let input = r#""config" { "pwdrequired" "1" }"#;
        let kv = parse_text_kv(input).unwrap();
        let config: Config = from_kv(&kv).unwrap();
        assert!(config.pwdrequired);
    }
}