Skip to content
/ fieldr Public

Boilerplate code killer. Generates various code, such as getters/setters, builders, or constants.

License

Notifications You must be signed in to change notification settings

m4gshm/fieldr

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Fieldr

Generator of various enumerated constants, types, functions based on a type properties like name, structure fields, tags or base type nature.

Supported commands

  • fields-to-consts - generate constants based on template applied to struct fields.

  • get-set - generates getters, setters for a structure type.

  • builder - generates builder API of a structure type.

  • as-map - generates a method or functon that converts the struct type to a map.

  • enrich-const-type - extends a constants type by 'get name' method, 'enum all values' function and 'get a constant by a value of the underlying type' function.

Installation

go install github.com/m4gshm/fieldr@latest

or

go install github.com/m4gshm/fieldr@HEAD

fields-to-consts example

source entity.go:

package enum_const

//go:generate fieldr -type Entity fields-to-consts -val tag.json -list jsons
type Entity struct {
    Id   int    `json:"id"`
    Name string `json:"name"`
}

then running this command in the same directory:

go generate .

will be generated entity_fieldr.go file with the next content:

// Code generated by 'fieldr'; DO NOT EDIT.

package enum_const

const (
    entityJsonId   = "id"
    entityJsonName = "name"
)

func jsons() []string {
    return []string{entityJsonId, entityJsonName}
}

this consist of constants based on the json tag contents and the method jsons that enumerates these constants.

To get extended help of the command, use the following:

fieldr fields-to-consts help

Example of generating ORM elements:

source entity.go:

package enum_const_db

//go:generate fieldr -type Entity
//go:fieldr fields-to-consts -name "'col' + field.name" -val "tag.db" -flat Versioned -type column -list . -ref-access .
//go:fieldr fields-to-consts -name "'pk' + field.name" -val "tag.db" -include "tag.pk != nil" -type column -list pk

type Entity struct {
    BaseEntity
    Versioned *VersionedEntity
    Name      string `db:"name"`
}

type BaseEntity struct {
    ID int32 `db:"id" pk:""`
}

type VersionedEntity struct {
    Version int64 `db:"version"`
}

generated entity_fieldr.go:

// Code generated by 'fieldr'; DO NOT EDIT.

package enum_const_db

type column string

const (
    colID      column = "id"
    colVersion column = "version"
    colName    column = "name"
    pkID       column = "id"
)

func columns() []column {
    return []column{colID, colVersion, colName}
}

func (s *Entity) ref(f column) any {
    if s == nil {
        return nil
    }
    switch f {
    case colID:
        return &s.BaseEntity.ID
    case colVersion:
        if v := s.Versioned; v != nil {
            return &v.Version
        }
    case colName:
        return &s.Name
    }
    return nil
}

func pk() []column {
    return []column{pkID}
}

explanation of used args:

  • -name "'col' + field.name" - defines constant names as 'col' appended by the associated field name.

  • -val "tag.db" - defines the value of a constant as a copy of the db tag of the associated field name.

  • -flat Versioned - also uses the VersionedEntity fields as constants source type in addition to the base Entity type.

  • -type column - adds the column type, and uses it as the type of the generated constants.

  • -list . - generates the columns function that returns constant values. It can be used to build sql queries like INSERT, SELECT.

  • -ref-access . - generates the ref method that provides access to the filed values, returns a reference pointing to the field associated with the constant. The method can be used in conjunction with Row.Scan from sql package.

  • -include "tag.pk != nil" - uses only 'pk' tag having a value.

get-set usage example

source entity.go

package get_set

import "time"

//go:generate fieldr -type Entity get-set

type BaseEntity[ID any] struct {
    id ID
}

type Entity[ID any] struct {
    *BaseEntity[ID]
    name    string
    surname string
    ts      time.Time
}
go generate .

generates entity_fieldr.go

// Code generated by 'fieldr'; DO NOT EDIT.

package get_set

import "time"

func (e *Entity[ID]) Id() ID {
    if e != nil {
        if be := e.BaseEntity; be != nil {
            return be.id
        }
    }

    var no ID
    return no
}

func (e *Entity[ID]) SetId(id ID) {
    if e != nil {
        if be := e.BaseEntity; be != nil {
            be.id = id
        }
    }
}

func (e *Entity[ID]) Name() string {
    if e != nil {
        return e.name
    }

    var no string
    return no
}

func (e *Entity[ID]) SetName(name string) {
    if e != nil {
        e.name = name
    }
}

func (e *Entity[ID]) Surname() string {
    if e != nil {
        return e.surname
    }

    var no string
    return no
}

func (e *Entity[ID]) SetSurname(surname string) {
    if e != nil {
        e.surname = surname
    }
}

func (e *Entity[ID]) Ts() time.Time {
    if e != nil {
        return e.ts
    }

    var no time.Time
    return no
}

func (e *Entity[ID]) SetTs(ts time.Time) {
    if e != nil {
        e.ts = ts
    }
}

builder usage example

source entity.go

package builder

//go:generate fieldr -type Entity builder -deconstructor .
type Entity[ID any] struct {
    *Model[ID]
    Name string
}

type Model[ID any] struct {
    ID        ID
    CreatedAt int64
    UpdatedAt int64
}
go generate .

generates entity_fieldr.go

// Code generated by 'fieldr'; DO NOT EDIT.

package builder

type EntityBuilder[ID any] struct {
    iD        ID
    createdAt int64
    updatedAt int64
    name      string
}

func NewEntityBuilder[ID any]() *EntityBuilder[ID] {
    return &EntityBuilder[ID]{}
}

func (b *EntityBuilder[ID]) Build() *Entity[ID] {
    if b == nil {
        return &Entity[ID]{}
    }
    return &Entity[ID]{
        Model: &Model[ID]{
            ID:        b.iD,
            CreatedAt: b.createdAt,
            UpdatedAt: b.updatedAt,
        },
        Name: b.name,
    }
}

func (b *EntityBuilder[ID]) ID(iD ID) *EntityBuilder[ID] {
    if b != nil {
        b.iD = iD
    }
    return b
}

func (b *EntityBuilder[ID]) CreatedAt(createdAt int64) *EntityBuilder[ID] {
    if b != nil {
        b.createdAt = createdAt
    }
    return b
}

func (b *EntityBuilder[ID]) UpdatedAt(updatedAt int64) *EntityBuilder[ID] {
    if b != nil {
        b.updatedAt = updatedAt
    }
    return b
}

func (b *EntityBuilder[ID]) Name(name string) *EntityBuilder[ID] {
    if b != nil {
        b.name = name
    }
    return b
}

func (e *Entity[ID]) ToBuilder() *EntityBuilder[ID] {
    if e == nil {
        return &EntityBuilder[ID]{}
    }
    var (
        Model_ID        ID
        Model_CreatedAt int64
        Model_UpdatedAt int64
    )
    if m := e.Model; m != nil {
        Model_ID = m.ID
        Model_CreatedAt = m.CreatedAt
        Model_UpdatedAt = m.UpdatedAt
    }

    return &EntityBuilder[ID]{
        iD:        Model_ID,
        createdAt: Model_CreatedAt,
        updatedAt: Model_UpdatedAt,
        name:      e.Name,
    }
}

as-map usage example

source struct.go

package asmap

import "time"

//go:generate fieldr -type EmbeddedAddress -out address_as_map.go as-map -key-type . -export
//go:generate fieldr -type Struct -out struct_as_map.go as-map -key-type . -export -rewrite type:*EmbeddedAddress:fmt=%v.AsMap() -flat Flat

type BaseStruct struct {
    ID int
    TS *time.Time
}

type EmbeddedAddress struct {
    ZipCode     int
    AddressLine string
}

type FlatPart struct {
    CardNum string
    Bank    string
}

type Struct[n string] struct {
    *BaseStruct
    Name     n
    Surname  string
    noExport string //nolint
    NoTag    string
    Address  *EmbeddedAddress
    Flat     FlatPart
}
go generate .

generates two files struct_as_map.go, address_as_map.go

// Code generated by 'fieldr'; DO NOT EDIT.

package asmap

type StructField string

const (
    BaseStructID StructField = "ID"
    BaseStructTS StructField = "TS"
    Name         StructField = "Name"
    Surname      StructField = "Surname"
    NoTag        StructField = "NoTag"
    Address      StructField = "Address"
    FlatCardNum  StructField = "CardNum"
    FlatBank     StructField = "Bank"
)

func (s *Struct[n]) AsMap() map[StructField]any {
    if s == nil {
        return nil
    }
    m := map[StructField]any{}
    if bs := s.BaseStruct; bs != nil {
        m[BaseStructID] = bs.ID
    }
    if bs := s.BaseStruct; bs != nil {
        if ts := bs.TS; ts != nil {
            m[BaseStructTS] = ts
        }
    }
    m[Name] = s.Name
    m[Surname] = s.Surname
    m[NoTag] = s.NoTag
    if a := s.Address; a != nil {
        m[Address] = a.AsMap()
    }
    m[FlatCardNum] = s.Flat.CardNum
    m[FlatBank] = s.Flat.Bank
    return m
}
// Code generated by 'fieldr'; DO NOT EDIT.

package asmap

type EmbeddedAddressField string

const (
    ZipCode     EmbeddedAddressField = "ZipCode"
    AddressLine EmbeddedAddressField = "AddressLine"
)

func (e *EmbeddedAddress) AsMap() map[EmbeddedAddressField]any {
    if e == nil {
        return nil
    }
    m := map[EmbeddedAddressField]any{}
    m[ZipCode] = e.ZipCode
    m[AddressLine] = e.AddressLine
    return m
}

enrich-const-type usage example

source enum.go

package enrich_enum

//go:generate fieldr -type Enum enrich-const-type -export

type Enum int

const (
    AA Enum = iota + 1
    BB
    CC
    DD
)

//go:generate fieldr -type StringEnum enrich-const-type -export

type BaseStringEnum string
type StringEnum BaseStringEnum

const (
    FIRST  StringEnum = "first one"
    SECOND StringEnum = "one more"
    THIRD  StringEnum = "any third"
)
go generate .

generates enum_fieldr.go

// Code generated by 'fieldr'; DO NOT EDIT.

package enrich_enum

func (e Enum) Name() string {
    switch e {
    case AA:
        return "AA"
    case BB:
        return "BB"
    case CC:
        return "CC"
    case DD:
        return "DD"
    default:
        return ""
    }
}

func EnumAll() []Enum {
    return []Enum{
        AA,
        BB,
        CC,
        DD,
    }
}

func EnumByName(name string) (e Enum, ok bool) {
    ok = true
    switch name {
    case "AA":
        e = AA
    case "BB":
        e = BB
    case "CC":
        e = CC
    case "DD":
        e = DD
    default:
        ok = false
    }
    return
}

func EnumByValue(value int) (e Enum, ok bool) {
    ok = true
    switch value {
    case 1:
        e = AA
    case 2:
        e = BB
    case 3:
        e = CC
    case 4:
        e = DD
    default:
        ok = false
    }
    return
}

and stringenum_fieldr.go

// Code generated by 'fieldr'; DO NOT EDIT.

package enrich_enum

func (s StringEnum) Name() string {
    switch s {
    case FIRST:
        return "FIRST"
    case SECOND:
        return "SECOND"
    case THIRD:
        return "THIRD"
    default:
        return ""
    }
}

func StringEnumAll() []StringEnum {
    return []StringEnum{
        FIRST,
        SECOND,
        THIRD,
    }
}

func StringEnumByName(name string) (e StringEnum, ok bool) {
    ok = true
    switch name {
    case "FIRST":
        e = FIRST
    case "SECOND":
        e = SECOND
    case "THIRD":
        e = THIRD
    default:
        ok = false
    }
    return
}

func StringEnumByValue(value string) (e StringEnum, ok bool) {
    ok = true
    switch value {
    case "first one":
        e = FIRST
    case "one more":
        e = SECOND
    case "any third":
        e = THIRD
    default:
        ok = false
    }
    return
}

See more examples here