GithubHelp home page GithubHelp logo

etecs-ru / typedyaml Goto Github PK

View Code? Open in Web Editor NEW
9.0 1.0 0.0 158 KB

YAML with polymorphic types in Go with this code generation tool.

Home Page: https://pkg.go.dev/github.com/etecs-ru/typedyaml

License: MIT License

Makefile 17.34% Go 82.66%
go codegen codegenerator yaml configuration polymorphic

typedyaml's Introduction

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 UsersGateway and OrdersGateway structures in our Go code, like:

package config

import "time"

type UsersGateway 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 users=UsersGateway orders=OrdersGateway
type Gateway interface {
	TypedYAML(*GatewayTyped) string
}

Now, run go generate. Generated struct GatewayTyped 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: users
  value:
    enabled: false
    host: internal-users.microservice.lan
    port: 8443
    user: robot
    security_descriptor: SY
gateways:
- type: users
  value:
    enabled: true
    host: external-users.microservice.lan
    port: 8443
    user: robot
    security_descriptor: SY
- type: orders
  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

typedyaml's People

Contributors

franchb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

typedyaml's Issues

Generate tests also

This generator could possibly gain the ability to generate tests for generated code.

Such generated tests could possibly help to detect small bugs or inconsistencies, like in such issue -- volatiletech/sqlboiler#758

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.