typedyaml

command module
v0.1.5 Latest Latest
Warning

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

Go to latest
Published: Feb 16, 2021 License: MIT Imports: 11 Imported by: 0

README

typedyaml

A lightweight code generator for Go alleviates YAML marshalling/unmarshalling unrelated structs in typed fashion. Depends on github.com/goccy/go-yaml allowing such benefits as fields validation.

Badges

Build Status Go Reference Go Report Card codecov License

Imagine, that you need to configure several instances of some service, each of a different kind using a YAML object with some key Config. The value of this field can correspond to two structs of your Go program: FooConfig and BarConfig. So, the field Config in your struct must be able to hold a value of two possible types. In this case, you have the following options:

  1. You can declare field Config as interface{} and somehow determine what type you should expect, assign an object of this type to Config and then unmarshal an object.
  2. You can unmarshal field Config separately.
  3. You can implement custom MarshalYAML/UnmarshalYAML for the third type that automatically will handle these cases.

This package provides means to generate all boilerplate code for the third case.

Usage

typedyaml [OPTION] NAME...

Options:

  • -interface string

    Name of the interface that encompasses all types.

  • -output string

    Output path where generated code should be saved.

  • -package string

    The package name in the generated file (default to GOPACKAGE).

  • -typed string

    The name of the struct that will be used for typed interface (default to {{interface}}{{Typed}}).

Each name in position argument should be the name of the struct. You can set an alias for struct name like this: foo=*FooConfig.

Example

See code in the /example folder.

For example, you have some microservice that serves orders to users and use gateways to other microservices that provides it. You need to configure it in one YAML file in a smart way.

You define UserGateway and OrdersGateway structures in our Go code, like:

package config

import "time"

type UserGateway struct {
	Enabled            bool   `yaml:"enabled"`
	Host               string `yaml:"host"`
	Port               int    `yaml:"port"`
	User               string `yaml:"user"`
	SecurityDescriptor string `yaml:"security_descriptor"`
}

type OrdersGateway struct {
	Enabled bool               `yaml:"enabled"`
	Kafka   KafkaConfiguration `yaml:"kafka"`
}

type KafkaConfiguration struct {
	Hosts          []string      `yaml:"hosts" validate:"required"`
	Topics         []string      `yaml:"topics" validate:"required"`
	GroupID        string        `yaml:"group_id" validate:"required"`
	ClientID       string        `yaml:"client_id"`
	ConnectBackoff time.Duration `yaml:"connect_backoff" validate:"min=0"`
	ConsumeBackoff time.Duration `yaml:"consume_backoff" validate:"min=0"`
	WaitClose      time.Duration `yaml:"wait_close" validate:"min=0"`
	MaxWaitTime    time.Duration `yaml:"max_wait_time"`
	IsolationLevel int           `yaml:"isolation_level"`
	Username       string        `yaml:"username"`
	Password       string        `yaml:"password"`
}

So you need to group these pieces together in some MicroserviceConfig structure and want to read it from YAML file.

We propose to implement polymorphic type Gateway using this code generation tool.

First, you must declare an interface that will hold either of these structs. The interface must have the method TypedYAML with a signature holding name of your container struct with a Typed suffix, like Gateway:GatewayTyped. This method will advise the compiler to work with types.

Let's see:

package config 

//go:generate go run github.com/etecs-ru/typedyaml -package config -interface Gateway UserGateway OrdersGateway
type Gateway interface {
	TypedYAML(*GatewayTyped) string
}

Now, run go generate. Generated struct ConfigTyped will have special implemented methods MarshalYAML/UnmarshalYAML. GatewayTyped could be used as a single instance, or in a slice. Adding configuration for new gateways now working like a charm -- just add its type to code generation argument and regenerate the code.

Let us write some configuration example:

tags:
  key: value
gateway:
  type: UserGateway
  value:
    enabled: false
    host: internal-users.microservice.lan
    port: 8443
    user: robot
    security_descriptor: SY
gateways:
- type: UserGateway
  value:
    enabled: true
    host: external-users.microservice.lan
    port: 8443
    user: robot
    security_descriptor: SY
- type: OrdersGateway
  value:
    enabled: true
    kafka:
      hosts:
        - host-a:7891
        - host-b:7892
      topics:
        - topic-a
        - topic-b
        - topic-c
      group_id: gid01
      client_id: cid01
      connect_backoff: 1000000000
      consume_backoff: 1000000000
      wait_close: 1000000000
      max_wait_time: 1000000000
      isolation_level: 0
      username: user
      password: password

You can use generated code like this:

package config

import (
	"io/ioutil"

	"github.com/goccy/go-yaml"
)

type MicroserviceConfig struct {
	Tags     map[string]string `yaml:"tags"`
	Gateway  GatewayTyped      `yaml:"gateway"`
	Gateways []GatewayTyped    `yaml:"gateways"`
}

func ReadMicroserviceConfigFromFile(path string) (*MicroserviceConfig, error) {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	var cfg MicroserviceConfig
	if err = yaml.Unmarshal(b, &cfg); err != nil {
		return nil, err
	}
	return &cfg, nil
}

func WriteMicroserviceConfigToFile(cfg MicroserviceConfig, path string) error {
	b, err := yaml.Marshal(cfg)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(path, b, 0644)
}

Contributing

Bug reports, test cases, and code contributions are more than welcome. Please refer to the contribution guidelines.

License

MIT License

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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