transform

package module
v0.0.0-...-e56f2aa Latest Latest
Warning

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

Go to latest
Published: Nov 21, 2022 License: MIT Imports: 3 Imported by: 0

README

transform

Description

transform can make a golang object transform its fields or be mapped to another object by tagging.

And also registering more transformers is possible.

It uses generic so Go1.18+ needed.

Tag

transform uses tag to execute transformation commands. Default tag name is transform and it can be changed.

The basic shape of tagging is like below:

type Person struct {
	Name   string `transform:"upper,bytes"`
	Age    int    `transform:"x2,x:3,big"`
}

You can check the tag by Tag method, and can change this by SetTag method.

// declare transformer
transformer := transform.New()

// check tag (default: 'transform')
tag := transformer.Tag()
fmt.Println("tag is", tag)

// change tag
transformer.SetTag("sample")
// then now you only can use this transformer to parse 'sample' tag

// change tag when declare
transformer := transform.New().SetTag("hello")

Tag is composed of multiple commands with comma(,).

If a transformation needs a parameter, you can define it using colon(:).

type Sample struct {
    A int `transform:"add1"`       // this just runs 'add1' transformer
    B int `transform:"add:1"`      // this runs 'add' transformer and give a parameter '1'
    C int `transform:"add1,add:1"` // multiple tags also allowed (combined with comma)
}

Register

Custome transformers can be registered by RegisterTransformer method. This method receives a name and transformation function. The transformation function only can be implemented by the function F1 and F2.

F1 receives one generic type, so the type of input and output is same. This is used for just transformation.

type Sample struct {
    V1 int `transform:"add:10"`
    V2 int `transform:"add:3"`
}

transformer.RegisterTransformer("add", transform.F1[int](func(i int, s string) int {
    x, _ := strconv.ParseInt(s, 10, 32)
    return i + int(x)
}))

Above example is registering 'add' transformer. This will update fields as the given parameter. The second parameter of the function F1 and F2 is, as you already knew, the parameter.

The only difference between F1 and F2 is a number of generic type. F2 can be used when mapping, so it needs input type and output type seperately.

type Original struct {
    V int `transform:"big"`
}

type Mapped struct {
    V *big.Int
}

transformer.RegisterTransformer("big", transform.F2(func(i int, s string) *big.Int {
	return big.NewInt(int64(i))
}))

Transformer also can be registered when initializing.

transformer := transform.New(
    transform.I{
        Name: "x2",
        F: transform.F1(func(i int, s string) int {
            return i * 2
        }),
    },
    transform.I{
        Name: "x",
        F: transform.F1(func(i int, s string) int {
            x, _ := strconv.ParseInt(s, 10, 32)
            return i * int(x)
        }),
    },
)

Transform

Transform transforms an object. It returns error when there was an error.

type Sample struct {
    S string `transform:"upper"`
}

sample := Sample{"hello"}
transformer := transform.New()
if err := transformer.Transform(&sample); err != nil {
    panic(err)
}

fmt.Println(sample) // "HELLO"

Mapping

mapping maps src to dst. It returns error when there was an error.

map option can target a field of dst. If you would like to map nested structure, use dot(.) to set its path.

Use - to ignore field mapping.

type Original struct {
    F1 string
    F2 string `transform:"map:F2.A,upper"`
    F3 string `transform:"map:-"`
    F4 int    `transform:"F3"`
}

type Destination struct {
    F1 string
    F2 struct {
        A string
    }
    F3 int
}

Also no problem to convert type.

type Original struct {
    V int `transform:"big"`
}

type Mapped struct {
    V *big.Int
}

transformer := transform.New()
transformer.RegisterTransformer("big", transform.F2(func(i int, s string) *big.Int {
	return big.NewInt(int64(i))
}))

original := Original{10}
var mapped Mapped

if err := transformer.Mapping(&original, &mapped); err != nil {
    panic(err)
}

fmt.Println(mapped) // {V: *big.Int(10)}

DTO example

This example can be found in transform_test.go.

// dtom means DTO (data transfer object) Mapper
type TransactionDtom struct {
    Sender string `json:"sender" transform:"trim0x,lower"`
    Amount string `json:"amount" transform:"big"`
}

// TransactionDtom will be mapped to this
type TransactionDto struct {
    Sender string
    Amount *big.Int
}

a := transform.New()
a.RegisterTransformer("trim0x", transform.F2(func(s1, s2 string) string {
    s1 = strings.TrimPrefix(s1, "0x")
    s1 = strings.ToLower(s1)
    return s1
}))
a.RegisterTransformer("big", transform.F2(func(s1, s2 string) *big.Int {
    i := new(big.Int)
    i.SetString(s1, 10)
    return i
}))

// raw data -> [unmarshal] -> dtom -> [transform] -> dto
tx := `{
    "sender":"0x4d943a7C1f2AF858BfEe8aB499fbE76B1D046eC7",
    "amount":"436799733113079832970000"
}`

var transactionDtom TransactionDtom
err := json.Unmarshal([]byte(tx), &transactionDtom)
if err != nil {
    panic(err)
}

var transactionDto TransactionDto
err = a.Mapping(&transactionDtom, &transactionDto)
if err != nil {
    panic(err)
}

// {sender: 4d943a7c1f2af858bfee8ab499fbe76b1d046ec7, amount: 436799733113079832970000}
t.Log(transactionDto)

Entity to Response example

type TransactionEntity struct {
    Id       int      `transform:"map:-"`
    Sender   string   `transform:"add0x,map:From"`
    Receiver string   `transform:"add0x,map:To"`
    Amount   *big.Int `transform:"str"`
}

type TransactionResponse struct {
    From   string `json:"from"`
    To     string `json:"to"`
    Amount string `json:"amount"`
}

a := transform.New()
a.RegisterTransformer("add0x", transform.F2(func(s1, s2 string) string {
    return "0x" + s1
}))
a.RegisterTransformer("str", transform.F2(func(i *big.Int, s string) string {
    return i.String()
}))

tx := &TransactionEntity{
    Id:       12345,
    Sender:   "4d943a7c1f2af858bfee8ab499fbe76b1d046ec7",
    Receiver: "fcba8de0706abf76e98d9ebeecbb42c29ab42ac3",
    Amount:   big.NewInt(436799733113079),
}

var transactionResponse TransactionResponse
err := a.Mapping(tx, &transactionResponse)
if err != nil {
    panic(err)
}

// {"from":"0x4d943a7c1f2af858bfee8ab499fbe76b1d046ec7","to":"0xfcba8de0706abf76e98d9ebeecbb42c29ab42ac3","amount":"436799733113079"}
s, _ := json.Marshal(&transactionResponse)
t.Log(string(s))

How to use

go get github.com/p9595jh/transform
import "github.com/p9595jh/transform"

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func F1

func F1[T any](f func(T, string) T) f

Input type and output type is same.

func F2

func F2[T, U any](f func(T, string) U) f

Input type and output type is different. This can be used for mapping.

Types

type I

type I struct {
	Name string
	F    f
}

Item for transforming

type Transformer

type Transformer interface {
	Transform(a any) error
	Mapping(src, dst any) (err error)
	// Convert(src, dst any) (err error)
	RegisterTransformer(name string, f f)
	SetTag(tag string) Transformer
	Tag() string
}

func New

func New(transformers ...I) Transformer

Jump to

Keyboard shortcuts

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