jconf

package module
v0.0.0-...-059b1e2 Latest Latest
Warning

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

Go to latest
Published: May 10, 2021 License: MIT Imports: 7 Imported by: 64

README

gone/jconf

GoDoc GoReportCard Coverage

Modular JSON config parsing allowing // comments and full serialization of the entire resulting data hierarchy to JSON.

Package jconf allows you to avoid having you main configuration data structure know about every individual code modules internal configuration structures in order to serialize the entire configuration. Instead you can localize module config data structures with the code using them.

Example

Below is a complete example using the comment-filtering pre-processor:

import (
	"github.com/One-com/gone/jconf"
	"encoding/json"
	"os"
	"bytes"
	"log"
)

type AppConfig struct {
	A string
	S *jconf.MandatorySubConfig
}

type ModuleConfig struct {
	B string
}

func initSubModule(cfg jconf.SubConfig) {
	var jc *ModuleConfig
	err := cfg.ParseInto(&jc)
	if err != nil {
		log.Fatal("Module Config parsing failed")
	}
}

var confdata2 = `// start comment
{
"a" : "app",
// comment
"s" : {
   "b" : "x // y" // end line comment
  }
}`

func main() {

	// main application conf object
	cfg := &AppConfig{}

	buf := bytes.NewBufferString(confdata2)

	err := jconf.ParseInto(buf, &cfg)
	if err != nil {
		log.Fatal(err.Error())
	}

	// Let our submodule parse its own config
	initSubModule(cfg.S)

	var out bytes.Buffer
	b, err := json.Marshal(cfg)
	if err != nil {
		log.Fatalf("Marshal error: %s", err.Error())
	}

	err = json.Indent(&out, b, "", "    ")
	if err != nil {
		log.Fatalf("Indent error: %s", err.Error())
	}
	out.WriteTo(os.Stdout)
}

Below is an example using only the subconfig feature:

import (
	"github.com/One-com/gone/jconf"
	"encoding/json"
	"os"
	"bytes"
	"log"
)

type AppConfig struct {
	A string
	S *jconf.MandatorySubConfig
}

type ModuleConfig struct {
	B string
}

var confdata = `{ "a" : "app", "s" : {"b": "module"}}`

func initSubModule(cfg jconf.SubConfig) {
	var jc *ModuleConfig
	err := cfg.ParseInto(&jc)
	if err != nil {
		log.Fatal("Module Config parsing failed")
	}
}

func main() {
	// main application conf object
	cfg := &AppConfig{}

	// make a JSON decoder
	dec := json.NewDecoder(bytes.NewBuffer([]byte(confdata)))
	dec.UseNumber()

	// Parse the man config
	err := dec.Decode(&cfg)
	if err != nil {
		log.Fatal(err.Error())
	}

	// Let our submodule parse its own config
	initSubModule(cfg.S)

	var out bytes.Buffer
	b, err := json.Marshal(cfg)
	if err != nil {
		log.Fatalf("Marshal error: %s", err)
	}

    err = json.Indent(&out, b, "", "    ")
	if err != nil {
		log.Fatalf("Indent error: %s", err)
	}
	out.WriteTo(os.Stdout)
}

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrEmptySubConfig = errors.New("Missing mandatory SubConfig")

ErrEmptySubConfig is returned my ParseInto on MandatorySubConfig if there is no JSON data.

Functions

func ParseInto

func ParseInto(source io.Reader, dest interface{}) (err error)

ParseInto loads a JSON stream into a destination object, which is a datatype defined by the caller. It will ignore values in the file it can't fit into dst. On a parsing error, it returns an error with the line and the location in the input data.

The input JSON data stream is allowed to contain line comments in the C++ style where // outside a JSON string object denotes that the rest of the line is a comment.

Example
package main

import (
	"bytes"
	"encoding/json"
	"log"
	"os"
)

type MyConfig struct {
	A string
	S FurtherConfig
}

type FurtherConfig struct {
	B string
}

var confdata2 = `// start comment
{
"a" : "app",
// comment
"s" : {
   "b" : "x // y" // end line comment
  }
}`

func main() {

	// main application conf object
	cfg := &MyConfig{}

	buf := bytes.NewBufferString(confdata2)

	err := ParseInto(buf, &cfg)
	if err != nil {
		log.Fatalf("%#v\n", err)
	}

	var out bytes.Buffer
	b, err := json.Marshal(cfg)
	if err != nil {
		log.Fatalf("Marshal error: %s", err.Error())
	}

	err = json.Indent(&out, b, "", "    ")
	if err != nil {
		log.Fatalf("Indent error: %s", err.Error())
	}
	out.WriteTo(os.Stdout)

}
Output:

{
    "A": "app",
    "S": {
        "B": "x // y"
    }
}

Types

type Duration

type Duration struct {
	time.Duration
}

Duration allows you to have nice durations in your json config, like "10s"

func (Duration) MarshalJSON

func (d Duration) MarshalJSON() (b []byte, err error)

MarshalJSON implements the stdlib encoding/json.Marshaler interface for Duration

func (*Duration) UnmarshalJSON

func (d *Duration) UnmarshalJSON(b []byte) (err error)

UnmarshalJSON implements the stdlib encoding/json.Unmarshaler interface for Duration

type MandatorySubConfig

type MandatorySubConfig subConfig

MandatorySubConfig implements the SubConfig interface which will return ErrEmptySubConfig if there is no JSON config. This can help catch mistakes in the config.

func (*MandatorySubConfig) MarshalJSON

func (m *MandatorySubConfig) MarshalJSON() ([]byte, error)

MarshalJSON allows the subconfig to be serialized

func (*MandatorySubConfig) ParseInto

func (m *MandatorySubConfig) ParseInto(i interface{}) (err error)

ParseInto on will Unmarshal the SubConfig JSON data into the provided interface If called on a nil value (no JSON data) will return ErrEmptySubConfig

type OptionalSubConfig

type OptionalSubConfig subConfig

OptionalSubConfig implements the SubConfig interface and silently ignores being passed nil JSON values. Users of OptionalSubConfig will have to check the resulting Config object passed to ParseInto for being nil after it returns

func DefaultSubConfig

func DefaultSubConfig() *OptionalSubConfig

DefaultSubConfig returns a placeholder value which can be assigned to any field which is an OptionalSubConfig to indicate that if the value is missing it should be assumed to be an empty JSON object ("{}") and passed into any ParseInto() call on the SubConfig. This allows for default values to be specified for entire subtrees of the JSON

Example
// B is the only value explicitly configured
var confdata = `
{
  "B" : "bar"
}`

// main application conf object
cfg := &App2Config{
	A: "foo",
	C: DefaultSubConfig(),
}

buf := bytes.NewBufferString(confdata)

err := ParseInto(buf, &cfg)
if err != nil {
	log.Fatal(err)
}

mcfg := &Module2Config{
	X: "xxx",
	Y: DefaultSubConfig(),
}

// Let our submodule parse its own config
err = cfg.C.ParseInto(&mcfg)
if err != nil {
	log.Fatalf("Module Config parsing failed: %s", err.Error())
}

ecfg := &ExtraConfig{Q: "qqq"}

// ... and the extra config
err = mcfg.Y.ParseInto(&ecfg)
if err != nil {
	log.Fatalf("Module Config parsing failed: %s", err.Error())
}

var out bytes.Buffer
b, err := json.Marshal(cfg)
if err != nil {
	log.Fatalf("Marshal error: %s", err)
}

err = json.Indent(&out, b, "", "    ")
if err != nil {
	log.Fatalf("Indent error: %s", err)
}
out.WriteTo(os.Stdout)
Output:

{
    "A": "foo",
    "B": "bar",
    "C": {
        "X": "xxx",
        "Y": {
            "Q": "qqq"
        }
    }
}

func (*OptionalSubConfig) MarshalJSON

func (m *OptionalSubConfig) MarshalJSON() ([]byte, error)

MarshalJSON allows the subconfig to be serialized

func (*OptionalSubConfig) ParseInto

func (m *OptionalSubConfig) ParseInto(i interface{}) (err error)

ParseInto on will Unmarshal the SubConfig JSON data into the provided interface If called on a nil value (no JSON data) will do nothing.

type SubConfig

type SubConfig interface {
	ParseInto(interface{}) error
	MarshalJSON() ([]byte, error)
}

SubConfig is an interface type which can passed sub module code. It contains the raw JSON data for the module config and can be asked to parse that JSON into a module specific Config object. The SubConfig will remember the parsed result so any later Marshal'ing of the full Config will include the sub module Config without the higher level code/config knowing about its structure.

Example
// main application conf object
cfg := &AppConfig{}

// make a JSON decoder
dec := json.NewDecoder(bytes.NewBuffer([]byte(confdata)))
dec.UseNumber()

// Parse the man config
err := dec.Decode(&cfg)
if err != nil {
	log.Fatalf("%v\n", err)
}

// Let our submodule parse its own config
err = initSubModule(cfg.S)
if err != nil {
	log.Fatalf("Module Config parsing failed: %s", err.Error())
}

var out bytes.Buffer
b, err := json.Marshal(cfg)
if err != nil {
	log.Fatalf("Marshal error: %s", err)
}

err = json.Indent(&out, b, "", "    ")
if err != nil {
	log.Fatalf("Indent error: %s", err)
}
out.WriteTo(os.Stdout)
Output:

{
    "A": "app",
    "S": {
        "B": "module"
    }
}

type SyntaxError

type SyntaxError struct {
	Cause *json.SyntaxError
	// contains filtered or unexported fields
}

SyntaxError is an extension of encoding/json.SyntaxError but with an error text with a better description of the position in the input data (marker and line)

func (*SyntaxError) Error

func (e *SyntaxError) Error() string

Jump to

Keyboard shortcuts

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