wayle-core 0.1.2

Core reactive primitives and D-Bus utilities for Wayle services
Documentation
#[cfg(feature = "schema")]
use std::borrow::Cow;
use std::fmt::{self, Debug, Formatter};

#[cfg(feature = "schema")]
use schemars::{JsonSchema, Schema, SchemaGenerator};
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use super::Property;

impl<T: Clone + Send + Sync + Debug + 'static> Debug for Property<T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        f.debug_struct("Property")
            .field("value", &self.get())
            .finish()
    }
}

impl<T: Clone + Send + Sync + Serialize + 'static> Serialize for Property<T> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        self.get().serialize(serializer)
    }
}

impl<'de, T: Clone + Send + Sync + Deserialize<'de> + 'static> Deserialize<'de> for Property<T> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let value = T::deserialize(deserializer)?;
        Ok(Property::new(value))
    }
}

#[cfg(feature = "schema")]
impl<T: Clone + Send + Sync + JsonSchema + 'static> JsonSchema for Property<T> {
    fn schema_name() -> Cow<'static, str> {
        T::schema_name()
    }

    fn json_schema(generator: &mut SchemaGenerator) -> Schema {
        T::json_schema(generator)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn serializes_to_inner_value() {
        let property = Property::new(42);
        let json = serde_json::to_string(&property).unwrap();

        assert_eq!(json, "42");
    }

    #[test]
    fn deserializes_from_inner_value() {
        let property: Property<String> = serde_json::from_str("\"hello\"").unwrap();

        assert_eq!(property.get(), "hello");
    }

    #[test]
    fn json_roundtrip() {
        #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
        struct Config {
            name: Property<String>,
            count: Property<i32>,
            enabled: Property<bool>,
        }

        let config = Config {
            name: Property::new(String::from("test")),
            count: Property::new(42),
            enabled: Property::new(true),
        };

        let json = serde_json::to_string(&config).unwrap();
        let deserialized: Config = serde_json::from_str(&json).unwrap();

        assert_eq!(deserialized.name.get(), "test");
        assert_eq!(deserialized.count.get(), 42);
        assert!(deserialized.enabled.get());
    }

    #[test]
    fn toml_roundtrip() {
        #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
        struct ClockConfig {
            format: Property<String>,
            show_seconds: Property<bool>,
        }

        let config = ClockConfig {
            format: Property::new(String::from("%H:%M")),
            show_seconds: Property::new(false),
        };

        let serialized = toml::to_string(&config).unwrap();
        assert!(serialized.contains("format = \"%H:%M\""));
        assert!(serialized.contains("show_seconds = false"));

        let deserialized: ClockConfig = toml::from_str(&serialized).unwrap();
        assert_eq!(deserialized.format.get(), "%H:%M");
        assert!(!deserialized.show_seconds.get());
    }

    #[test]
    fn deserialized_property_starts_with_no_subscribers() {
        let property: Property<i32> = serde_json::from_str("42").unwrap();

        assert!(!property.has_subscribers());
    }
}