gear-objects 0.3.0

Rust component object model
Documentation
use crate::*;
use paste::paste;
// use serde::de;
// use serde::ser;
use serde::ser::Serializer;
use serde::Deserializer;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
// TODO get rid of erased_serde

fn get_persist_data<S>(component: &Component) -> Result<HashMap<String, String>, S::Error>
where
    S: Serializer,
{
    // TODO add a note that TypeId might work but can break between rust releases
    // TODO add a note about why keys are used
    // TODO may want a check that keys are unique
    use serde::ser::Error;
    let mut data = HashMap::new();
    // TODO would have to duplicate this for PersistBinary
    // or just switch based on is_human_readable
    for persist in find_repeated_trait!(component, PersistString) {
        let key = persist.persist_key();
        let value = persist.save().map_err(Error::custom)?;
        data.insert(key, value);
    }
    Ok(data)
}

impl Serialize for Component {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        use serde::ser::SerializeTuple;

        match serializer.serialize_tuple(2) {
            // TODO can we simplify this (as in deserialize)?
            Ok(mut s) => {
                let ids = self.object_ids();
                s.serialize_element(&ids)?;

                let data = get_persist_data::<S>(self)?;
                s.serialize_element(&data)?;

                s.end()
            }
            Err(err) => Err(err),
        }
    }
}

// let boxed = Box::new($object);
// let obj_ptr = Box::into_raw(boxed);
// $component.add_trait::<dyn $trait1, $obj_type>(
//     [<get_ $trait1:lower _id>](),
//     $obj_ptr);
// $component.add_object::<$obj_type>(
//     [<get_ $obj_type:lower _id>](),
//     obj_ptr);

// TODO
// how do we know which traits are associated with an object?
//    could save that in component
//    but we really need the Trait type so we can properly erase it
// need a way to add an object to a component based on TypeId
//    for now if id == get_dog_id() then add a Dog::default()
// need to deserialize the tupe and the map (probably requires a visitor)
impl<'de> serde::Deserialize<'de> for Component {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        type Data = (Vec<TypeId>, HashMap<String, String>);
        let data: Data = Deserialize::deserialize(deserializer)?;

        let mut component = Component::new("loaded"); // TODO persist tag?
        for id in data.0 {
            if id == get_dog_id() {
                component.add_object(obj_id, obj_ptr)
            }
        }
        // deserializer.deserialize_tuple(2, visitor);

        Ok(component)
        // type Types = Vec<TypeId>;
        // let c = Types::deserialize(deserializer)?;
        // let ps = Component {
        //     objects: c,
        //     boxes: HashMap::new(),
        // };
        // Ok(ps)
    }
}

pub trait PersistString {
    fn persist_key(&self) -> String;

    fn save(&self) -> Result<String, String>;

    fn load(&mut self, str: &str) -> Result<(), String>;
}
register_type!(PersistString);

#[derive(Default, Serialize, Deserialize)]
struct Dog {
    breed: String,
}
register_type!(Dog);

impl PersistString for Dog {
    fn persist_key(&self) -> String {
        "Dog".to_owned()
    }

    fn save(&self) -> Result<String, String> {
        Ok(self.breed.clone())
    }

    fn load(&mut self, str: &str) -> Result<(), String> {
        // Overkill to create a new Dog here but that's more typical of real code.
        let mut new = Dog {
            breed: str.to_owned(),
        };
        std::mem::swap(self, &mut new);
        Ok(())
    }
}
register_type!(Perist);

/*
#[derive(Serialize, Deserialize)]
struct PersistStrings {
    // TODO: choose this one if is_human_readable()
    // TODO: need a version for binary serialization
    ids: Vec<ComponentId>,
    data: HashMap<String, String>,
}

pub struct StringData {
    pub key: String,
    pub data: String,
}

pub trait SaveStrings {
    /// Implementations should use a unique key for the result. Typically this will be the
    /// concrete type name.
    fn save(&self) -> Result<StringData, String>;
    fn serial<S>(&self, s: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer;
}

pub trait LoadStrings {
    /// Use the key returned by save to index into table.
    fn load(&mut self, table: &HashMap<String, String>) -> Result<(), String>;
    fn deserial<'a, D>(&mut self, deserializer: D) -> Result<(), D::Error>
    where
        D: serde::Deserializer<'a>;
}

#[derive(Serialize, Deserialize)]
struct Dog {
    breed: String,
}

impl SaveStrings for Dog {
    fn save(&self) -> Result<StringData, String> {
        let data = StringData {
            key: "foo".to_owned(),
            data: "bar".to_owned(),
        };
        Ok(data)
    }

    fn serial<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        self.serialize(serializer)
    }
}

impl LoadStrings for Dog {
    fn load(&mut self, _table: &HashMap<String, String>) -> Result<(), String> {
        Ok(())
    }

    fn deserial<'a, D>(&mut self, deserializer: D) -> Result<(), D::Error>
    where
        D: serde::Deserializer<'a>,
    {
        let mut new = Dog::deserialize(deserializer)?;
        std::mem::swap(self, &mut new);
        Ok(())
    }
}

impl Serialize for Component {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let ps = PersistStrings {
            ids: Vec::new(),      // TODO: record all the old object IDs
            data: HashMap::new(), // TODO: populate with SaveStrings
        };
        ps.serialize(serializer)
    }
}

impl<'de> serde::Deserialize<'de> for Component {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let ps = PersistStrings::deserialize(deserializer)?;

        let component = Component::new("loaded");
        // TODO: populate objects with ids (probably need a macro to register a function for this)
        //       can we register this with the Component? should we?
        // TODO: call LoadStrings for everything
        Ok(component)
    }
}
 */