gofigure

package module
v0.0.0-...-59ad0ff Latest Latest
Warning

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

Go to latest
Published: Oct 13, 2023 License: AGPL-3.0 Imports: 14 Imported by: 0

README

Package gofigure parses configuration files. An application might use this instead of parsing JSON, YAML, or many other alternatives.

Our primary goals include:

  • Provide a human-friendly configuration file format.
  • Minimize third-party dependencies.

Our definition of "human-friendly" is opinionated. It basically means a configuration file format that supports comments, and is not sensitive to whitespace or indentation.

The motivation to avoid third-party dependencies is to minimize the amount of code that needs to be audited, in particular for secure applications where the size of a software bill of materials may be a concern.

To accomplish these goals, the configuration syntax is based on the Go (golang) language. This allows us to use Go's parser, already part of the standard library, rather than third-party imports. And Go's syntax meets our standard for user-friendliness.

Example Configuration

The supported syntax supports, for example,

local = env {
  port: 8080,
  host: "127.0.0.1",
}

prod = env {
  port: 80,
  host: "0.0.0.0",
}

// environment tells the application which of the envs to use by default.
environment = local

Example Application Code

Gofigure provides a multiplexer, with a simple scheme in which a handler is called for each value in a configuration file.

A handler is called if it is the best match for a "path" in the configuration file. In the example above, "local = env {}" is a fully-qualified path that matchs only the first item, while "env" is a path that matches all the env{...} objects.

mux := gofigure.Mux{
  "env": gofigure.Object(handleEnv),
  // ...
}

Above, "env" is the path to match, HandleEnv is the application-provided handler function, and gofigure.Object instructs gofigure to unmarshal the value as an object. Under the hood, gofigure uses "encoding/json" for decoding into Go structs, and Object, Array, and Scalar tell gofigure what kind of value to expect.

// env is the Go struct to decode into
type env struct {
  Host string
  Port int
}

// handleEnv shows the signature of a handler function for env objects.
func handleEnv(r gofigure.Route, v env) error {...}

For each match in the configuration file, a handler is passed the full route and value. In this example, a route would be ["local", "=", "env", "{}"] and the same handler would be called again with a route starting "prod".

Documentation

Overview

Package gofigure parses configuration files. An application might use this instead of parsing JSON, YAML, or many other alternatives.

Our primary goals include:

  • Provide a human-friendly configuration file format.
  • Minimize third-party dependencies.

Our definition of "human-friendly" is opinionated. It basically means a configuration file format that supports comments, and is not sensitive to whitespace or indentation.

The motivation to avoid third-party dependencies is to minimize the amount of code that needs to be audited, in particular for secure applications where the size of a software bill of materials may be a concern.

To accomplish these goals, the configuration syntax is based on the Go (golang) language. This allows us to use Go's parser, already part of the standard library, rather than third-party imports. And Go's syntax meets our standard for user-friendliness.

Example Configuration

The supported syntax supports, for example,

local = env {
  port: 8080,
  host: "127.0.0.1",
}

prod = env {
  port: 80,
  host: "0.0.0.0",
}

// environment tells the application which of the envs to use by default.
environment = local

Example Application Code

Gofigure provides a multiplexer, with a simple scheme in which a handler is called for each value in a configuration file.

A handler is called if it is the best match for a "path" in the configuration file. In the example above, "local = env {}" is a fully-qualified path that matchs only the first item, while "env" is a path that matches all the `env{...}` objects.

mux := gofigure.Mux{
  "env": gofigure.Object(handleEnv),
  // ...
}

Above, "env" is the path to match, `HandleEnv` is the application-provided handler function, and `gofigure.Object` instructs gofigure to unmarshal the value as an object. Under the hood, gofigure uses "encoding/json" for decoding into Go structs, and Object, Array, and Scalar tell gofigure what kind of value to expect.

// env is the Go struct to decode into
type env struct {
  Host string
  Port int
}

// handleEnv shows the signature of a handler function for env objects.
func handleEnv(r gofigure.Route, v env) error {...}

For each match in the configuration file, a handler is passed the full route and value. In this example, a route would be ["local", "=", "env", "{}"] and the same handler would be called again with a route starting "prod".

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func FileToJSON

func FileToJSON(out io.Writer, filename string, opt ...Option) error

func NewConfiguration

func NewConfiguration() *gofigure

func ToJSON

func ToJSON(out io.Writer, in io.Reader, opt ...Option) (err error)

Types

type Assign

type Assign func(key string, value interface{}) error

An Assignment is called when gofigure parses an ast.ValueSpec, such as "x = something".

type Compose

type Compose func(ident string, value map[string]interface{}) (interface{}, error)

A Compose callback is called on an ast.KeyValueExpr, such as "foo{first: x, second: y}".

type Concat

type Concat func(ident string, value ...interface{}) (interface{}, error)

A Concat callback is called on an ast.CompositeLit, such as "f(1, 2, 3)"

type DecodeHandler

type DecodeHandler func(r Route, dec *json.Decoder) error

type Error

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

func (Error) Error

func (e Error) Error() string

func (Error) Position

func (e Error) Position() token.Position

func (Error) Prefix

func (e Error) Prefix() string

type Handler

type Handler interface {
	// contains filtered or unexported methods
}

A Handler is created by passing a function to Scalar, Array, or Object.

func Array

func Array[S ~[]T, T any](h func(r Route, payload S) error) Handler

Array defines a handler which will be passed gofigure payloads. To produce the payload, gofigure encodes a value as JSON, and decodes the JSON into a variable. The JSON is encoded within square brackets, so it is treated a a single variable with zero or more items.

func Object

func Object[T any](h func(r Route, payload T) error) Handler

Object defines a handler which will be passed gofigure payloads. To produce the payload, gofigure encodes a value as JSON, and decodes the JSON into a variable. The JSON is encoded within curly brackets, so it is treated a a single struct variable.

func Scalar

func Scalar[T any](h func(r Route, payload T) error) Handler

Scalar defines a handler which will be passed gofigure payloads. To produce the payload, gofigure encodes a value as JSON, and decodes the JSON into a variable.

type Mux

type Mux map[string]Handler

A Mux is multiplexer that calls a handler for each configuration payload parsed. The Mux can have multiple routes, each with a handler. During [Parse], the Mux attempts to match each item in a configuraiton file with an appropriate handler.

Example
package main

import (
	"fmt"
	"strings"

	"src.d10.dev/gofigure"
)

const configfile = `
local = env {
  port: 8080,
  host: "127.0.0.1",
}

prod = env {
  port: 80,
  host: "0.0.0.0",
}

// environment tells the application which of the envs to use by default.
environment = local
`

type env struct {
	Host string
	Port int
}

func HandleEnv(r gofigure.Route, v env) error {
	fmt.Printf("env %q is %s:%d\n", r[0], v.Host, v.Port)
	return nil
}

func HandleEnvironment(_ gofigure.Route, v string) error {
	fmt.Printf("default environment is %q\n", v)
	return nil
}

func main() {
	mux := gofigure.Mux{
		"environment": gofigure.Scalar(HandleEnvironment),
		"env":         gofigure.Object(HandleEnv),
	}

	err := mux.Parse("configfile", mux.Source(strings.NewReader(configfile)))
	if err != nil {
		fmt.Println(err)
	}

}
Output:

env "local" is 127.0.0.1:8080
env "prod" is 0.0.0.0:80
default environment is "local"

func (Mux) Parse

func (m Mux) Parse(filename string, opt ...ParseOption) error

Parse attempts to visit each item in a configuration file, decoding a value and invoking a hander for each item.

func (*Mux) Source

func (*Mux) Source(r io.Reader) ParseOption

type Option

type Option func(*walk) error

func WithFilename

func WithFilename(fname string) Option

func WithOuterBracket

func WithOuterBracket(b OuterBracket) Option

type OuterBracket

type OuterBracket uint
const (
	OuterBracketNone OuterBracket = iota
	OuterBracketArray
	OuterBracketObject
)

type ParseOption

type ParseOption func(p *parse) error

type Route

type Route []string

A Route provides a handler with information about where a payload appears in configuration.

If configuration is `f = g(x)`, the route will include "f", "=", "g", and "()".

if configuration is only `g{x}`, the route will include "g" and "{}".

if configuration is `f = x`, the route will include "f" and "=".

func (Route) String

func (r Route) String() string

type Stream

type Stream func(Route, *json.Decoder) error

Stream allows an application to define a handler that is passed a json.Decoder.

Directories

Path Synopsis
cmd
gofigure
gofigure command is intended to help troubleshoot configuration files using the gofigure syntax.
gofigure command is intended to help troubleshoot configuration files using the gofigure syntax.

Jump to

Keyboard shortcuts

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