azriel91/peace

`ParamsSpecs`: Make serialized form more concise

azriel91 opened this issue · 1 comments

Write custom serialization / deserialization code so that we don't get the extra layer of indentation.

Current:

app_download: !Value
  value:
    src: https://github.com/azriel91/web_app/releases/download/0.1.1/web_app.tar
    dest: azriel91/web_app/0.1.1/web_app.tar
    marker: null
s3_bucket: !FieldWise
  field_wise_spec:
    name: !Value
      value: azriel-peace-envman-demo-1
    marker: null

Desired:

app_download: !Value
  src: https://github.com/azriel91/web_app/releases/download/0.1.1/web_app.tar
  dest: azriel91/web_app/0.1.1/web_app.tar
s3_bucket: !FieldWise
  name: !Value
    value: azriel-peace-envman-demo-1

Tuple params fieldwise specs are tricky.

Current:

something: !FieldWise
  field_wise_spec: !MappingFn
    field_name: _0
    fn_map: Some(Fn(&bool, &u16) -> Option<Vec<u8>>)
    marker: null

Desired:

something: !FieldWise
  _0: !MappingFn
    fn_map: Some(Fn(&bool, &u16) -> Option<Vec<u8>>)

May be too magic to do this. Tried a few approaches:

  • #[serde(flatten)] on ParamsSpec::<T>::Value and ParamsSpec::FieldWise:

    • does not work if T has unnamed fields.
    • message: Error("can only flatten structs and maps (got a sequence)").
  • manually serializing ParamsSpec:

    Started on this, but it's weird when you get to ParamsSpec::Value, ideally you serialize self as an enum variant for the !Tag, but then get T to serialize its fields, which I don't think can be done:

    impl<T> Serialize for ParamsSpec<T>
    where
        T: Params + Clone + Debug + Serialize + Send + Sync + 'static,
    {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
        {
            match self {
                ParamsSpec::Stored => serializer.serialize_unit_variant("ParamsSpec", 0, "Stored"),
                ParamsSpec::Value { value } => {
                    match T::type_kind() {
                        TypeKind::Unit => serializer.serialize_unit_variant("ParamsSpec", 1, "Value"),
                        TypeKind::Tuple => serializer.serialize_tuple_variant("ParamsSpec", 1, "Value", value.fields_unnamed()),
                        TypeKind::Struct => serializer.serialize_struct_variant("ParamsSpec", 1, "Value", value.fields_named()),
                        TypeKind::Enum => todo!(),
                    }
                }
                ParamsSpec::InMemory => serializer.serialize_unit_variant("ParamsSpec", 2, "InMemory"),
                ParamsSpec::MappingFn(value) => serializer.serialize_newtype_variant("ParamsSpec", 3, "MappingFn", value),
                ParamsSpec::FieldWise { field_wise_spec } => todo!(),
            }
        }
    }
    
    /// The kind of type this is.
    ///
    /// Needed to determine how to serialize/deserialize a type at runtime.
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    pub enum TypeKind {
        /// Unit struct with no fields.
        Unit,
        /// Tuple struct with unnamed fields.
        Tuple,
        /// Tuple struct with named fields.
        Named,
        /// Enum.
        Enum,
    }