diff --git a/pkg/codec/config.go b/pkg/codec/config.go index 27ac85500..8d8703aa2 100644 --- a/pkg/codec/config.go +++ b/pkg/codec/config.go @@ -23,6 +23,7 @@ import ( // - extract element -> [ElementExtractorModifierConfig] // - epoch to time -> [EpochToTimeModifierConfig] // - address to string -> [AddressBytesToStringModifierConfig] +// - wrapper -> [WrapperModifierConfig] type ModifiersConfig []ModifierConfig func (m *ModifiersConfig) UnmarshalJSON(data []byte) error { @@ -55,6 +56,8 @@ func (m *ModifiersConfig) UnmarshalJSON(data []byte) error { (*m)[i] = &PropertyExtractorConfig{} case ModifierAddressToString: (*m)[i] = &AddressBytesToStringModifierConfig{} + case ModifierWrapper: + (*m)[i] = &ModifiersConfig{} default: return fmt.Errorf("%w: unknown modifier type: %s", types.ErrInvalidConfig, mType) } @@ -88,6 +91,7 @@ const ( ModifierEpochToTime ModifierType = "epoch to time" ModifierExtractProperty ModifierType = "extract property" ModifierAddressToString ModifierType = "address to string" + ModifierWrapper ModifierType = "wrapper" ) type ModifierConfig interface { @@ -248,6 +252,29 @@ func (c *AddressBytesToStringModifierConfig) MarshalJSON() ([]byte, error) { }) } +// WrapperModifierConfig wraps fields into subfields of a new struct. +type WrapperModifierConfig struct { + // Fields key defines the fields to be wrapped and the name of the wrapper struct. + // The field becomes a subfield of the wrapper struct where the name of the subfield is map value. + Fields map[string]string +} + +func (r *WrapperModifierConfig) ToModifier(_ ...mapstructure.DecodeHookFunc) (Modifier, error) { + fields := map[string]string{} + for i, f := range r.Fields { + // using a private variable will make the field not serialize, essentially dropping the field + fields[upperFirstCharacter(f)] = fmt.Sprintf("dropFieldPrivateName%d", i) + } + return NewWrapperModifier(r.Fields), nil +} + +func (r *WrapperModifierConfig) MarshalJSON() ([]byte, error) { + return json.Marshal(&modifierMarshaller[WrapperModifierConfig]{ + Type: ModifierWrapper, + T: r, + }) +} + type typer struct { Type string } diff --git a/pkg/codec/wrapper.go b/pkg/codec/wrapper.go new file mode 100644 index 000000000..4fc8cdada --- /dev/null +++ b/pkg/codec/wrapper.go @@ -0,0 +1,36 @@ +package codec + +import "reflect" + +// NewWrapperModifier wraps fields into subfields of a new struct where the name of the struct is field name and the subfield name is map value. +func NewWrapperModifier(fields map[string]string) Modifier { + m := &wrapperModifier{ + modifierBase: modifierBase[string]{ + fields: fields, + onToOffChainType: map[reflect.Type]reflect.Type{}, + offToOnChainType: map[reflect.Type]reflect.Type{}, + }, + } + + m.modifyFieldForInput = func(_ string, field *reflect.StructField, _ string, fieldName string) error { + field.Type = reflect.StructOf([]reflect.StructField{{ + Name: fieldName, + Type: field.Type, + }}) + return nil + } + + return m +} + +type wrapperModifier struct { + modifierBase[string] +} + +func (t *wrapperModifier) TransformToOnChain(offChainValue any, _ string) (any, error) { + return offChainValue, nil +} + +func (t *wrapperModifier) TransformToOffChain(onChainValue any, _ string) (any, error) { + return onChainValue, nil +}