codec

package
v0.0.0-...-47b5856 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 1, 2024 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Example

Example demonstrates how to use the codec package. It will make use of each Modifier provided in the package, along with their config.

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"math"
	"time"

	"github.com/O1MaGnUmO1/chainlink-common/pkg/codec"
	"github.com/O1MaGnUmO1/chainlink-common/pkg/types"
)

// This example demonstrates how you can have config for itemTypes with one codec,
// one that is modified before encoding for on-chain and modified after decoding for off-chain
// the other is left unmodified during encoding and decoding.
const (
	anyUnmodifiedTypeName     = "Unmodified"
	anyModifiedStructTypeName = "SecondItem"
)

var _ types.RemoteCodec = &ExampleStructJSONCodec{}

type ExampleStructJSONCodec struct{}

func (ExampleStructJSONCodec) Encode(_ context.Context, item any, _ string) ([]byte, error) {
	return json.Marshal(item)
}

func (ExampleStructJSONCodec) GetMaxEncodingSize(_ context.Context, n int, _ string) (int, error) {
	// not used in the example, and not really valid for json.
	return math.MaxInt32, nil
}

func (ExampleStructJSONCodec) Decode(_ context.Context, raw []byte, into any, _ string) error {
	err := json.Unmarshal(raw, into)
	if err != nil {
		return fmt.Errorf("%w: %s", types.ErrInvalidType, err)
	}
	return nil
}

func (ExampleStructJSONCodec) GetMaxDecodingSize(ctx context.Context, n int, _ string) (int, error) {
	// not used in the example, and not really valid for json.
	return math.MaxInt32, nil
}

func (ExampleStructJSONCodec) CreateType(_ string, _ bool) (any, error) {
	// parameters here are unused in the example, but can be used to determine what type to expect.
	// this allows remote execution to know how to decode the incoming message
	// and for [codec.NewModifierCodec] to know what type to expect for intermediate phases.
	return &OnChainStruct{}, nil
}

type OnChainStruct struct {
	Aa int64
	Bb string
	Cc bool
	Dd string
	Ee int64
	Ff string
}

const config = `
[
  { "Type" : "drop", "Fields" :  ["Bb"] },
  { "Type" : "hard code", "OnChainValues" : {"Ff" :  "dog", "Bb" : "bb"}, "OffChainValues" : {"Zz" : "foo"}},
  { "Type" : "rename", "Fields" :  {"Aa" :  "Bb"}},
  { "Type" : "extract element", "Extractions" :  {"Dd" :  "middle"}},
  { "Type" : "epoch to time", "Fields" :  ["Ee"]}
]
`

// config converts the OnChainStruct to this structure
type OffChainStruct struct {
	Bb int64
	Cc bool
	Dd []string
	Ee *time.Time
	Zz string
}

// Example demonstrates how to use the codec package.
// It will make use of each [Modifier] provided in the package, along with their config.
func main() {
	mods, err := createModsFromConfig()
	if err != nil {
		fmt.Println(err)
		return
	}

	c, err := codec.NewModifierCodec(&ExampleStructJSONCodec{}, mods)
	if err != nil {
		fmt.Println(err)
		return
	}

	input := &OnChainStruct{
		Aa: 10,
		Bb: "20",
		Cc: true,
		Dd: "great example",
		Ee: 631515600,
		Ff: "dog",
	}

	ctx := context.Background()
	b, err := c.Encode(ctx, input, anyUnmodifiedTypeName)
	fmt.Println("Encoded: " + string(b))

	output := &OnChainStruct{}
	if err = c.Decode(ctx, b, output, anyUnmodifiedTypeName); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("Decoded: %+v\n", output)

	anyTimeEpoch := int64(631515600)
	t := time.Unix(anyTimeEpoch, 0)
	modifedInput := &OffChainStruct{
		Bb: 10,
		Cc: true,
		Dd: []string{"terrible example", "great example", "not this one :("},
		Ee: &t,
		Zz: "foo",
	}

	b, err = c.Encode(ctx, modifedInput, anyModifiedStructTypeName)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("Encoded with modifications: " + string(b))

	output2 := &OffChainStruct{}
	if err = c.Decode(ctx, b, output2, anyModifiedStructTypeName); err != nil {
		fmt.Println(err)
		return
	}

	fmt.Printf("Decoded wih modifications: %+v\n", output2)
}

func createModsFromConfig() (codec.Modifier, error) {
	modifierConfig := &codec.ModifiersConfig{}
	if err := json.Unmarshal([]byte(config), modifierConfig); err != nil {
		return nil, err
	}

	mod, err := modifierConfig.ToModifier()
	if err != nil {
		return nil, err
	}

	modByItemType := map[string]codec.Modifier{
		anyModifiedStructTypeName: mod,
		anyUnmodifiedTypeName:     codec.MultiModifier{},
	}

	return codec.NewByItemTypeModifier(modByItemType)
}
Output:

Encoded: {"Aa":10,"Bb":"20","Cc":true,"Dd":"great example","Ee":631515600,"Ff":"dog"}
Decoded: &{Aa:10 Bb:20 Cc:true Dd:great example Ee:631515600 Ff:dog}
Encoded with modifications: {"Aa":10,"Bb":"","Cc":true,"Dd":"great example","Ee":631515600,"Ff":"dog"}
Decoded wih modifications: &{Bb:10 Cc:true Dd:[great example] Ee:1990-01-05 05:00:00 +0000 UTC Zz:foo}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func BigIntHook

func BigIntHook(_, to reflect.Type, data any) (any, error)

BigIntHook is a mapstructure hook that converts number types to *math/big.Int and vice versa. Float values are cast to an int64 before being converted to a *math/big.Int.

func FitsInNBitsSigned

func FitsInNBitsSigned(n int, bi *big.Int) bool

FitsInNBitsSigned returns if the *math/big.Int can fit in n bits as a signed integer. Namely, if it's in the range [-2^(n-1), 2^(n-1) - 1]

func NewModifierCodec

func NewModifierCodec(codec types.RemoteCodec, modifier Modifier, hooks ...mapstructure.DecodeHookFunc) (types.RemoteCodec, error)

NewModifierCodec returns a codec that calls the modifier before calling codec functions. hooks are applied to the mapstructure decoding when Encode or Decode is called.

func SliceToArrayVerifySizeHook

func SliceToArrayVerifySizeHook(from reflect.Type, to reflect.Type, data any) (any, error)

SliceToArrayVerifySizeHook is a mapstructure hook that verifies if a slice is being assigned to an array, it will have the same number of elements. The default in mapstructure is to allow the slice to be smaller and will zero out the remaining elements in that case.

Types

type DropModifierConfig

type DropModifierConfig struct {
	Fields []string
}

DropModifierConfig drops all fields listed. The casing of the first character is ignored to allow compatibility. Note that unused fields are ignored by types.Codec. This is only required if you want to rename a field to an already used name. For example, if a struct has fields A and B, and you want to rename A to B, then you need to either also rename B or drop it.

func (*DropModifierConfig) MarshalJSON

func (d *DropModifierConfig) MarshalJSON() ([]byte, error)

func (*DropModifierConfig) ToModifier

type ElementExtractorLocation

type ElementExtractorLocation int

ElementExtractorLocation is used to determine which element to extract from a slice or array. The default is ElementExtractorLocationMiddle, which will extract the middle element. valid json values are "first", "middle", and "last".

const (
	ElementExtractorLocationFirst ElementExtractorLocation = iota
	ElementExtractorLocationMiddle
	ElementExtractorLocationLast
	ElementExtractorLocationDefault = ElementExtractorLocationMiddle
)

func (*ElementExtractorLocation) MarshalJSON

func (e *ElementExtractorLocation) MarshalJSON() ([]byte, error)

func (*ElementExtractorLocation) UnmarshalJSON

func (e *ElementExtractorLocation) UnmarshalJSON(b []byte) error

type ElementExtractorModifierConfig

type ElementExtractorModifierConfig struct {
	// Key is the name of the field to extract from and the value is which element to extract.
	Extractions map[string]*ElementExtractorLocation
}

ElementExtractorModifierConfig is used to extract an element from a slice or array

func (*ElementExtractorModifierConfig) MarshalJSON

func (e *ElementExtractorModifierConfig) MarshalJSON() ([]byte, error)

func (*ElementExtractorModifierConfig) ToModifier

type EpochToTimeModifierConfig

type EpochToTimeModifierConfig struct {
	Fields []string
}

EpochToTimeModifierConfig is used to convert epoch seconds as uint64 fields on-chain to time.Time

func (*EpochToTimeModifierConfig) MarshalJSON

func (e *EpochToTimeModifierConfig) MarshalJSON() ([]byte, error)

func (*EpochToTimeModifierConfig) ToModifier

type HardCodeModifierConfig

type HardCodeModifierConfig struct {
	OnChainValues  map[string]any
	OffChainValues map[string]any
}

HardCodeModifierConfig is used to hard code values into the map. Note that hard-coding values will override other values.

func (*HardCodeModifierConfig) MarshalJSON

func (h *HardCodeModifierConfig) MarshalJSON() ([]byte, error)

func (*HardCodeModifierConfig) ToModifier

func (h *HardCodeModifierConfig) ToModifier(onChainHooks ...mapstructure.DecodeHookFunc) (Modifier, error)

type Modifier

type Modifier interface {
	RetypeToOffChain(onChainType reflect.Type, itemType string) (reflect.Type, error)

	// TransformToOnChain transforms a type returned from AdjustForInput into the outputType.
	// You may also pass a pointer to the type returned by AdjustForInput to get a pointer to outputType.
	TransformToOnChain(offChainValue any, itemType string) (any, error)

	// TransformToOffChain is the reverse of TransformForOnChain input.
	// It is used to send back the object after it has been decoded
	TransformToOffChain(onChainValue any, itemType string) (any, error)
}

Modifier allows you to modify the off-chain type to be used on-chain, and vice-versa. A modifier is set up by retyping the on-chain type to a type used off-chain.

func NewByItemTypeModifier

func NewByItemTypeModifier(modByItemType map[string]Modifier) (Modifier, error)

NewByItemTypeModifier returns a Modifier that uses modByItemType to determine which Modifier to use for a given itemType.

func NewElementExtractor

func NewElementExtractor(fields map[string]*ElementExtractorLocation) Modifier

NewElementExtractor creates a modifier that extracts an element from a slice or array. fields is used to determine which fields to extract elements from and which element to extract. This modifier is lossy, as TransformToOffChain will always return a slice of length 1 with the single element, so calling TransformToOnChain, then TransformToOffChain will not return the original value, if it has multiple elements.

func NewEpochToTimeModifier

func NewEpochToTimeModifier(fields []string) Modifier

NewEpochToTimeModifier converts all fields from time.Time off-chain to int64.

func NewHardCoder

func NewHardCoder(onChain map[string]any, offChain map[string]any, hooks ...mapstructure.DecodeHookFunc) (Modifier, error)

NewHardCoder creates a modifier that will hard-code values for on-chain and off-chain types The modifier will override any values of the same name, if you need an overwritten value to be used in a different field, NewRenamer must be used before NewHardCoder.

func NewRenamer

func NewRenamer(fields map[string]string) Modifier

type ModifierConfig

type ModifierConfig interface {
	ToModifier(onChainHooks ...mapstructure.DecodeHookFunc) (Modifier, error)
}

type ModifierType

type ModifierType string
const (
	ModifierRename         ModifierType = "rename"
	ModifierDrop           ModifierType = "drop"
	ModifierHardCode       ModifierType = "hard code"
	ModifierExtractElement ModifierType = "extract element"
	ModifierEpochToTime    ModifierType = "epoch to time"
)

type ModifiersConfig

type ModifiersConfig []ModifierConfig

ModifiersConfig unmarshalls as a list of ModifierConfig by using a field called Type The values available for Type are case-insensitive and the config they require are below: - rename -> RenameModifierConfig - drop -> DropModifierConfig - hard code -> HardCodeModifierConfig - extract element -> ElementExtractorModifierConfig - epoch to time -> EpochToTimeModifierConfig

func (*ModifiersConfig) ToModifier

func (m *ModifiersConfig) ToModifier(onChainHooks ...mapstructure.DecodeHookFunc) (Modifier, error)

func (*ModifiersConfig) UnmarshalJSON

func (m *ModifiersConfig) UnmarshalJSON(data []byte) error

type MultiModifier

type MultiModifier []Modifier

MultiModifier is a Modifier that applies each element for the slice in-order (reverse order for TransformForOnChain).

func (MultiModifier) RetypeToOffChain

func (c MultiModifier) RetypeToOffChain(onChainType reflect.Type, itemType string) (reflect.Type, error)

func (MultiModifier) TransformToOffChain

func (c MultiModifier) TransformToOffChain(onChainValue any, itemType string) (any, error)

func (MultiModifier) TransformToOnChain

func (c MultiModifier) TransformToOnChain(offChainValue any, itemType string) (any, error)

type RenameModifierConfig

type RenameModifierConfig struct {
	Fields map[string]string
}

RenameModifierConfig renames all fields in the map from the key to the value The casing of the first character is ignored to allow compatibility of go convention for public fields and on-chain names.

func (*RenameModifierConfig) MarshalJSON

func (r *RenameModifierConfig) MarshalJSON() ([]byte, error)

func (*RenameModifierConfig) ToModifier

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL