Entity-less serialization

Back in the serde chapter, many possible formats were listed for serializing views.
By default, views will serialize separately. This has big advantages but serialized size is not one of them.
In this chapter we'll infer EntitiesViewMut content from other views instead of serializing it.

Serialization

For serialization, we can rely on the default derive format.
We only have to ignore EntitiesViewMut by using serde(skip).

use shipyard::{Component, EntitiesViewMut, ViewMut, WorldBorrow};

#[derive(Component, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
struct Name(String);

#[derive(Component, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
enum FavoriteLanguage {
    Rust,
}

#[derive(WorldBorrow, serde::Serialize)]
struct LanguagesViewMut<'v> {
    #[serde(skip)]
    entities: EntitiesViewMut<'v>,
    #[serde(borrow)]
    vm_name: ViewMut<'v, Name>,
    #[serde(borrow)]
    vm_favorite_language: ViewMut<'v, FavoriteLanguage>,
}

Deserialization

When serializing structs, serde will handle them as maps. Let's start there.
We'll use deserialize_in_place since views don't own their data.

impl<'tmp, 'view, 'de: 'view> serde::Deserialize<'de> for LanguagesViewMut<'view> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        panic!("LanguagesViewMut cannot be directly deserialized. Use deserialize_in_place instead.")
    }

    fn deserialize_in_place<D>(deserializer: D, place: &mut Self) -> Result<(), D::Error>
    where
        D: Deserializer<'de>,
    {
        struct LanguagesViewMutVisitor<'tmp, 'view> {
            place: &'tmp mut LanguagesViewMut<'view>,
        }
        impl<'tmp, 'view, 'de: 'view> serde::de::Visitor<'de> for LanguagesViewMutVisitor<'tmp, 'view> {
            type Value = ();

            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                formatter.write_str("a struct with vm_name and vm_favorite_language fields")
            }

            fn visit_map<A>(mut self, mut map: A) -> Result<(), A::Error>
            where
                A: serde::de::MapAccess<'de>,
            {
                todo!()
            }
        }

        deserializer.deserialize_struct(
            "LanguagesViewMut",
            &["vm_name", "vm_favorite_language"],
            LanguagesViewMutVisitor { place },
        )
    }
}

Now we can add vm_name and vm_favorite_language deserialization code by replacing the todo!() with:

while let Some(key) = map.next_key::<&str>()? {
    match key {
        "vm_name" => {
            map.next_value_seed(NameDeserializer { place: self.place })?;
        }
        "vm_favorite_language" => {
            map.next_value_seed(FavoriteLanguageDeserializer { place: self.place })?;
        }
        _ => {
            // Skip unknown fields
            map.next_value::<serde::de::IgnoredAny>()?;
        }
    }
}

We simply delegate to ViewMut's default deserialization for the rest.
(To be frank there might be a simpler way to go about this, but I don't know it)

struct NameDeserializer<'tmp, 'view> {
    place: &'tmp mut LanguagesViewMut<'view>,
}
impl<'tmp, 'view, 'de: 'view> serde::de::DeserializeSeed<'de> for NameDeserializer<'tmp, 'view> {
    type Value = ();

    fn deserialize<D>(self, deserializer: D) -> Result<(), D::Error>
    where
        D: Deserializer<'de>,
    {
        serde::Deserialize::deserialize_in_place(deserializer, &mut self.place.vm_name)
    }
}

struct FavoriteLanguageDeserializer<'tmp, 'view> {
    place: &'tmp mut LanguagesViewMut<'view>,
}
impl<'tmp, 'view, 'de: 'view> serde::de::DeserializeSeed<'de>
    for FavoriteLanguageDeserializer<'tmp, 'view>
{
    type Value = ();

    fn deserialize<D>(self, deserializer: D) -> Result<(), D::Error>
    where
        D: Deserializer<'de>,
    {
        serde::Deserialize::deserialize_in_place(
            deserializer,
            &mut self.place.vm_favorite_language,
        )
    }
}

We're done deserializing vm_name and vm_favorite_language. The final touch is to make the entities alive and return.

for eid in self
    .place
    .vm_name
    .iter()
    .ids()
    .chain(self.place.vm_favorite_language.iter().ids())
{
    self.place.entities.spawn(eid);
}

Ok(())

Test

Let's make sure everything works as expected.

#[test]
fn recipe_test() {
    use serde::Deserializer;
    use shipyard::{Component, EntitiesViewMut, IntoIter, ViewMut, World, WorldBorrow};

    let mut world = World::new();

    let eid1 = world.add_entity(Name("Alice".to_string()));
    let eid2 = world.add_entity((Name("Bob".to_string()), FavoriteLanguage::Rust));

    let serialized = world.run(|vm_favorite_language: LanguagesViewMut| {
        serde_json::to_string(&vm_favorite_language).unwrap()
    });

    drop(world);

    let mut new_world = World::new();

    let mut deserializer = serde_json::Deserializer::from_str(&serialized);
    new_world.deserialize::<_, LanguagesViewMut>(&mut deserializer);

    new_world.run(|mut vm_favorite_language: LanguagesViewMut| {
        assert!(vm_favorite_language.entities.is_alive(eid1));
        assert!(vm_favorite_language.entities.is_alive(eid2));

        assert_eq!(vm_favorite_language.vm_name[eid1].0, "Alice");
        assert_eq!(vm_favorite_language.vm_name[eid2].0, "Bob");
        assert_eq!(
            vm_favorite_language.vm_favorite_language[eid2],
            FavoriteLanguage::Rust
        );
    });

    // -----
    // -----
    // -----

    #[derive(Component, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
    struct Name(String);

    #[derive(Component, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
    enum FavoriteLanguage {
        Rust,
    }

    #[derive(WorldBorrow, serde::Serialize)]
    struct LanguagesViewMut<'v> {
        #[serde(skip)]
        entities: EntitiesViewMut<'v>,
        #[serde(borrow)]
        vm_name: ViewMut<'v, Name>,
        #[serde(borrow)]
        vm_favorite_language: ViewMut<'v, FavoriteLanguage>,
    }

    impl<'tmp, 'view, 'de: 'view> serde::Deserialize<'de> for LanguagesViewMut<'view> {
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where
            D: Deserializer<'de>,
        {
            panic!("LanguagesViewMut cannot be directly deserialized. Use deserialize_in_place instead.")
        }

        fn deserialize_in_place<D>(deserializer: D, place: &mut Self) -> Result<(), D::Error>
        where
            D: Deserializer<'de>,
        {
            struct LanguagesViewMutVisitor<'tmp, 'view> {
                place: &'tmp mut LanguagesViewMut<'view>,
            }
            impl<'tmp, 'view, 'de: 'view> serde::de::Visitor<'de> for LanguagesViewMutVisitor<'tmp, 'view> {
                type Value = ();

                fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                    formatter.write_str("a struct with vm_name and vm_favorite_language fields")
                }

                fn visit_map<A>(mut self, mut map: A) -> Result<(), A::Error>
                where
                    A: serde::de::MapAccess<'de>,
                {
                    struct NameDeserializer<'tmp, 'view> {
                        place: &'tmp mut LanguagesViewMut<'view>,
                    }
                    impl<'tmp, 'view, 'de: 'view> serde::de::DeserializeSeed<'de> for NameDeserializer<'tmp, 'view> {
                        type Value = ();

                        fn deserialize<D>(self, deserializer: D) -> Result<(), D::Error>
                        where
                            D: Deserializer<'de>,
                        {
                            serde::Deserialize::deserialize_in_place(
                                deserializer,
                                &mut self.place.vm_name,
                            )
                        }
                    }

                    struct FavoriteLanguageDeserializer<'tmp, 'view> {
                        place: &'tmp mut LanguagesViewMut<'view>,
                    }
                    impl<'tmp, 'view, 'de: 'view> serde::de::DeserializeSeed<'de>
                        for FavoriteLanguageDeserializer<'tmp, 'view>
                    {
                        type Value = ();

                        fn deserialize<D>(self, deserializer: D) -> Result<(), D::Error>
                        where
                            D: Deserializer<'de>,
                        {
                            serde::Deserialize::deserialize_in_place(
                                deserializer,
                                &mut self.place.vm_favorite_language,
                            )
                        }
                    }

                    while let Some(key) = map.next_key::<&str>()? {
                        match key {
                            "vm_name" => {
                                map.next_value_seed(NameDeserializer { place: self.place })?;
                            }
                            "vm_favorite_language" => {
                                map.next_value_seed(FavoriteLanguageDeserializer {
                                    place: self.place,
                                })?;
                            }
                            _ => {
                                // Skip unknown fields
                                map.next_value::<serde::de::IgnoredAny>()?;
                            }
                        }
                    }

                    for eid in self
                        .place
                        .vm_name
                        .iter()
                        .ids()
                        .chain(self.place.vm_favorite_language.iter().ids())
                    {
                        self.place.entities.spawn(eid);
                    }

                    Ok(())
                }
            }

            deserializer.deserialize_struct(
                "LanguagesViewMut",
                &["vm_name", "vm_favorite_language"],
                LanguagesViewMutVisitor { place },
            )
        }
    }
}