shouchan

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jun 20, 2023 License: Apache-2.0 Imports: 7 Imported by: 3

README

shouchan

CI PkgGoDev

Overview

Package shouchan provides simple configuration management for Golang application, with following features:

  • read configuration from command line flag and/or YAML file, mix&match, into a struct
  • 3 sources: default value, YAML file and flag
  • priority:in case of multiple source returns same config struct field, the preference is flag over YAML over default value
  • support following use cases:
    • simple: single action & single configuration, via SConf
    • multiple: multiple actions & multiple configurations, via ActionConf

include support following field types:

  • integer types
  • float type
  • string
  • time.Duration
  • and all types that implement encoding.TextMarshaler and encoding.TextUnmarshaler interface
  • there is also github.com/hujun-open/shouchantypes include some other types

Additional types could be supported by using Register, see types.go for example.

struct field naming:

  • YAML: lowercase
  • flag: lowercase, for nested struct field, it is "-<struct_fieldname_level_0>-<struct_fieldname_level_1>...", see example below:

Simple Example:

package main

import (
	"fmt"
	"net"
	"time"

	"github.com/hujun-open/shouchan"
	_ "github.com/hujun-open/shouchantypes"
)

type Company struct {
	//the usage tag is used for command line usage
	Name string `usage:"company name"`
}

type Employee struct {
	Name      string           `usage:"employee name"`
	Addr      string           `usage:"employee address"`
	IPAddr    net.IP           `usage:"employee IP address"`
	Subnet    net.IPNet        `usage:"employee IP subnet"`
	MAC       net.HardwareAddr `usage:"employee MAC address"`
	JointTime time.Time        `usage:"employee join time"`

	Employer Company
}

func main() {
	//default config
	def := Employee{
		Name:   "defName",
		Addr:   "defAddr",
		IPAddr: net.ParseIP("1.2.3.4"),
		MAC:    net.HardwareAddr{0x11, 0x22, 0x33, 0x44, 0x55, 0x66},
		Employer: Company{
			Name: "defCom",
		},
	}
	_, prefix, _ := net.ParseCIDR("192.168.1.0/24")
	def.Subnet = *prefix
	def.JointTime, _ = time.Parse(time.DateTime, "2023-01-02 13:22:33")
	cnf, err := shouchan.NewSConfCMDLine(&def, "")
	if err != nil {
		panic(err)
	}
	ferr, aerr := cnf.ReadwithCMDLine()
	fmt.Printf("ferr %v,aerr %v\n", ferr, aerr)
	fmt.Printf("final result is %+v\n", cnf.GetConf())
}

Output:

  • Usage
 .\test.exe -?
flag provided but not defined: -?
Usage:
  -f <filepath> : read from config file <filepath>
  -addr <string> : employee address
        default:defAddr
  -employer-name <string> : company name
        default:defCom
  -ipaddr <struct> : employee IP address
        default:1.2.3.4
  -jointtime <struct> : employee join time
        default:2023-01-02 13:22:33 +0000 UTC
  -mac <struct> : employee MAC address
        default:11:22:33:44:55:66
  -name <string> : employee name
        default:defName
  -subnet <struct> : employee IP subnet
        default:192.168.1.0/24
  • no command line args, no config file, default is used
 .\test.exe   
ferr <nil>,aerr <nil>
final result is &{Name:defName Addr:defAddr IPAddr:1.2.3.4 Subnet:{IP:192.168.1.0 Mask:ffffff00} MAC:11:22:33:44:55:66 JointTime:2023-01-02 13:22:33 +0000 UTC Employer:{Name:defCom}}
  • config file via "-f" command args, value from file take procedence
.\test.exe -f ..\..\testdata\test.yaml
ferr <nil>,aerr <nil>
final result is &{Name:nameFromFile Addr:addrFromFile IPAddr:1.2.3.4 Subnet:{IP:192.168.1.0 Mask:ffffff00} MAC:11:22:33:44:55:66 JointTime:2023-01-02 13:22:33 +0000 UTC Employer:{Name:comFromFile}}
  • mix command line args and config file, args to override employee name:
 .\test.exe -f ..\..\testdata\test.yaml -name nameFromArg
ferr <nil>,aerr <nil>
final result is &{Name:nameFromArg Addr:addrFromFile IPAddr:1.2.3.4 Subnet:{IP:192.168.1.0 Mask:ffffff00} MAC:11:22:33:44:55:66 JointTime:2023-01-02 13:22:33 +0000 UTC Employer:{Name:comFromFile}}
  • mix command line args and config file, args to override company name:
.\test.exe -f ..\..\testdata\test.yaml -employer-name comFromArg
ferr <nil>,aerr <nil>
final result is &{Name:nameFromFile Addr:addrFromFile IPAddr:1.2.3.4 Subnet:{IP:192.168.1.0 Mask:ffffff00} MAC:11:22:33:44:55:66 JointTime:2023-01-02 13:22:33 +0000 UTC Employer:{Name:comFromArg}}

Multiple Actions/Configurations Example

/*
This example demonstrate an zip file utility has two actions: show and zip, each action has different configuration via struct zipShowCfg and zipArchiveCfg.
*/
package main

import (
	"fmt"

	"github.com/hujun-open/shouchan"
)

type zipShowCfg struct {
	Zipf, Fname string
}

func (zscfg *zipShowCfg) Default() *zipShowCfg {
	return &zipShowCfg{
		Zipf:  "defaultShowZipf",
		Fname: "defaultShowFname",
	}
}

func (zacfg zipShowCfg) DefaultCfgFilePath() string {
	return ""
}

type zipArchiveCfg struct {
	Folder, Zipf string
}

func (zacfg *zipArchiveCfg) Default() *zipArchiveCfg {
	return &zipArchiveCfg{
		Zipf:   "defaultArchiveZipf",
		Folder: "defaultArchiveFolder",
	}
}

func (zacfg zipArchiveCfg) DefaultCfgFilePath() string {
	return ""
}

func main() {
	//create an ActionConf with a map 
	acnf, err := shouchan.NewActionConfWithCMDLine(map[string]shouchan.ConfigWithDefCfgFilePath{
		"show": (&zipShowCfg{}).Default(),
		"zip":  (&zipArchiveCfg{}).Default(),
	})
	if err != nil {
		panic(err)
	}
	err, ferr, aerr := acnf.ReadwithCMDLine()
	if err!=nil {
		panic(err)
	}
	fmt.Println("loading errors:",ferr, aerr)
	fmt.Printf("loaded action %v config is %+v", acnf.GetLoadedAction(), acnf.GetLoadedConf())
}

the command line looks like following:

Usage: <action> [<parameters...>]
Actions: show|zip
Action specific usage:
= show
Usage:
  -f <filepath> : read from config file <filepath>
  -fname <string> :
        default:defaultShowFname
  -zipf <string> :
        default:defaultShowZipf

= zip
Usage:
  -f <filepath> : read from config file <filepath>
  -folder <string> :
        default:defaultArchiveFolder
  -zipf <string> :
        default:defaultArchiveZipf

some CLI runs:

test.exe zip -folder testmy -zipf testmy.zip
<nil> <nil> <nil>
loaded action zip config is &{Folder:testmy Zipf:testmy.zip}
test.exe wrong
panic: wrong is not a valid action, use -? for list of actions

goroutine 1 [running]:
main.main()
        C:/hujun/gomodules/src/shouchan/cmd/test/main.go:50 +0x365

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Register

func Register[T any](to ToStr, from FromStr)

Register type T with provided to and from functions

Types

type Action

type Action struct {
	// contains filtered or unexported fields
}

type ActionConf

type ActionConf struct {
	// contains filtered or unexported fields
}

ActionConf represents a command with multiple actions, e.g. an zip file utility might have full command line structure:

- examplezip show -zipf <zip_filename> -fname <filename_inside_zip> - examplezip zip -folder <foldername> -zipf <zip_filename> - examplezip extract -zipf <zip_filename> -output <output_path>

"show/zip/extract" in this example represents different action, and each action requires different set of configuration/parameters, ActionConf allows user to specify a map betweening an action(string) with *SConf[any]

func NewActionConfWithCMDLine

func NewActionConfWithCMDLine(list map[string]ConfigWithDefCfgFilePath) (*ActionConf, error)

NewActionConfWithCMDLine creates ActionConf via list, key is the action, value is the a config struct implements ConfigWithDefCfgFilePath interface.

func (*ActionConf) GetLoadedAction

func (acnf *ActionConf) GetLoadedAction() string

GetLoadedAction returns the loaded action, "" means not loaded

func (*ActionConf) GetLoadedConf

func (acnf *ActionConf) GetLoadedConf() any

GetLoadedConf returns loaded SConf, nil means not loaded

func (*ActionConf) Read

func (acnf *ActionConf) Read(args []string) (actionerr, ferr, aerr error)

Read loads configuration from args and/or config file args format is following: - if there is only one action: the same as SConf.Read - otherwise, args[0] is the action, action is case-sensistive

return following errors: - actionerr: error during parsing action, application should check & handle this error - ferr&aerr: same sa SConf

func (*ActionConf) ReadwithCMDLine

func (acnf *ActionConf) ReadwithCMDLine() (actionerr, ferr, aerr error)

type ConfigWithDefCfgFilePath

type ConfigWithDefCfgFilePath interface {
	DefaultCfgFilePath() string //return "" to be ignored
}

type FromStr

type FromStr func(s string) (any, error)

FromStr is the function convert a string into a instance of to-be-supported-type

type SConf

type SConf[T any] struct {
	// contains filtered or unexported fields
}

SConf represents a set of configurations as a struct

func NewSConf

func NewSConf[T any](def T, defpath string, fset *flag.FlagSet) (*SConf[T], error)

NewSConf returns a new SConf instance, def is a pointer to configruation struct with default value, defpath is the default configuration file path, it could be overriden by using command line arg "-f", could be "" means no default path fset is the flagset to bind

func NewSConfCMDLine

func NewSConfCMDLine[T any](def T, defpath string) (*SConf[T], error)

NewSConfCMDLine is same as NewSConf, just use flag.CommandLine as the flagset

func (*SConf[T]) GetConf

func (cnf *SConf[T]) GetConf() T

GetConf returns config value

func (*SConf[T]) GetConfAny

func (cnf *SConf[T]) GetConfAny() any

func (*SConf[T]) MarshalYAML

func (cnf *SConf[T]) MarshalYAML() ([]byte, error)

MarshalYAML marshal config value into YAML

func (*SConf[T]) Read

func (cnf *SConf[T]) Read(args []string) (ferr, aerr error)

Read read configuration first from file, then flagset from args, flagset will be read regardless if file read succeds, ferr is error of file reading, aerr is error of flagset reading. if there is ferr and/or aerr, it could be treated as non-fatal failure thanks to mix&match and priority support.

func (*SConf[T]) ReadwithCMDLine

func (cnf *SConf[T]) ReadwithCMDLine() (ferr, aerr error)

ReadCMDLine is same as Read, expcept the args is os.Args[1:]

func (*SConf[T]) UnmarshalYAML

func (cnf *SConf[T]) UnmarshalYAML(buf []byte) error

UnmarshalYAML unmrshal YAML encoded buf into config value

type SConfInt

type SConfInt interface {
	Read(args []string) (ferr, aerr error)
	GetConfAny() any
	// contains filtered or unexported methods
}

type ToStr

type ToStr func(in any) (string, error)

ToStr is the function convert a instance of to-be-supported-type into string

Jump to

Keyboard shortcuts

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