Documentation ¶
Overview ¶
Package bkl implements a layered configuration language parser.
- Language & tool documentation: https://bkl.gopatchy.io/
- Go library source: https://github.com/gopatchy/bkl
- Go library documentation: https://pkg.go.dev/github.com/gopatchy/bkl
Example ¶
package main import ( "os" "github.com/gopatchy/bkl" ) func main() { // import "github.com/gopatchy/bkl" b := bkl.New() err := b.MergeFileLayers("tests/example1/service.test.toml") if err != nil { panic(err) } err = b.OutputToWriter(os.Stdout, "json") if err != nil { panic(err) } }
Output: {"addr":"127.0.0.1","name":"myService","port":8081}
Index ¶
- Variables
- func FileMatch(path string) (string, string, error)
- type Parser
- func (p *Parser) Document(index int) (any, error)
- func (p *Parser) MergeFile(path string) error
- func (p *Parser) MergeFileLayers(path string) error
- func (p *Parser) MergeParser(other *Parser) error
- func (p *Parser) MergePatch(index int, patch any) error
- func (p *Parser) NumDocuments() int
- func (p *Parser) Output(ext string) ([]byte, error)
- func (p *Parser) OutputIndex(index int, ext string) ([][]byte, error)
- func (p *Parser) OutputToFile(path, format string) error
- func (p *Parser) OutputToWriter(fh io.Writer, format string) error
- func (p *Parser) Outputs(ext string) ([][]byte, error)
- func (p *Parser) SetDebug(debug bool)
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // Base error; every error in bkl inherits from this Err = fmt.Errorf("bkl error") // Format and system errors ErrDecode = fmt.Errorf("decoding error (%w)", Err) ErrEncode = fmt.Errorf("encoding error (%w)", Err) ErrOutputFile = fmt.Errorf("error opening output file (%w)", Err) ErrInvalidIndex = fmt.Errorf("invalid index (%w)", Err) ErrInvalidFilename = fmt.Errorf("invalid filename (%w)", Err) ErrInvalidType = fmt.Errorf("invalid type (%w)", Err) ErrMissingFile = fmt.Errorf("missing file (%w)", Err) ErrNoMatchFound = fmt.Errorf("no document matched $match (%w)", Err) ErrRequiredField = fmt.Errorf("required field not set (%w)", Err) ErrUnknownFormat = fmt.Errorf("unknown format (%w)", Err) // Base language directive error ErrInvalidDirective = fmt.Errorf("invalid directive (%w)", Err) // Specific language directive errors ErrInvalidMergeType = fmt.Errorf("invalid $merge type (%w)", ErrInvalidDirective) ErrInvalidParentType = fmt.Errorf("invalid $parent type (%w)", ErrInvalidDirective) ErrInvalidPatchType = fmt.Errorf("invalid $patch type (%w)", ErrInvalidDirective) ErrInvalidPatchValue = fmt.Errorf("invalid $patch value (%w)", ErrInvalidDirective) ErrInvalidReplaceType = fmt.Errorf("invalid $replace type (%w)", ErrInvalidDirective) ErrMergeRefNotFound = fmt.Errorf("$merge reference not found (%w)", ErrInvalidDirective) ErrReplaceRefNotFound = fmt.Errorf("$replace reference not found (%w)", ErrInvalidDirective) )
Functions ¶
func FileMatch ¶ added in v1.0.2
FileMatch attempts to find a file with the same base name as path, but possibly with a different supported extension. It is intended to support "virtual" filenames that auto-convert from the format of the underlying real file.
Returns the real filename and the requested output format, or ("", "", error).
Types ¶
type Parser ¶
type Parser struct {
// contains filtered or unexported fields
}
A Parser reads input documents, merges layers, and generates outputs.
Terminology ¶
- Each Parser can read multiple files
- Each file represents a single layer
- Each file contains one or more documents
- Each document generates one or more outputs
Directive Evaluation Order ¶
Directive evaluation order can matter, e.g. if you $merge a subtree that contains an $output directive.
Merge phase 1 (load)
- $parent
Merge phase 2 (evaluate)
- $env
Merge phase 3 (merge)
- $patch
Output phase 1 (process)
- $merge
- $replace
Output phase 2 (output)
- $output
Example ¶
package main import ( "os" "github.com/gopatchy/bkl" ) func main() { b := bkl.New() // Also parses tests/example1/service.yaml err := b.MergeFileLayers("tests/example1/service.test.toml") if err != nil { panic(err) } if err = b.OutputToWriter(os.Stdout, "json"); err != nil { panic(err) } }
Output: {"addr":"127.0.0.1","name":"myService","port":8081}
func New ¶
func New() *Parser
New creates and returns a new Parser with an empty starting document set.
New always succeeds and returns a Parser instance.
Example ¶
package main import ( "fmt" "github.com/gopatchy/bkl" ) func main() { b := bkl.New() fmt.Println(b.NumDocuments()) }
Output: 0
func (*Parser) Document ¶
Document returns the parsed, merged tree for the document at index.
Example ¶
package main import ( "fmt" "github.com/gopatchy/bkl" ) func main() { b := bkl.New() if err := b.MergeFileLayers("tests/example1/service.test.toml"); err != nil { panic(err) } doc, err := b.Document(0) if err != nil { panic(err) } fmt.Println(doc) }
Output: map[addr:127.0.0.1 name:myService port:8081]
func (*Parser) MergeFile ¶
MergeFile parses the file at path and merges its contents into the Parser's document state using bkl's merge semantics.
Example ¶
package main import ( "os" "github.com/gopatchy/bkl" ) func main() { // Compare to Parser.MergeFileLayers example. b := bkl.New() // Does *not* parse tests/example1/service.yaml err := b.MergeFile("tests/example1/service.test.toml") if err != nil { panic(err) } if err = b.OutputToWriter(os.Stdout, "json"); err != nil { panic(err) } }
Output: {"port":8081}
func (*Parser) MergeFileLayers ¶
MergeFileLayers determines relevant layers from the supplied path and merges them in order.
Example ¶
package main import ( "os" "github.com/gopatchy/bkl" ) func main() { // Compare to Parser.MergeFile example. b := bkl.New() // Also parses tests/example1/service.yaml err := b.MergeFileLayers("tests/example1/service.test.toml") if err != nil { panic(err) } if err = b.OutputToWriter(os.Stdout, "json"); err != nil { panic(err) } }
Output: {"addr":"127.0.0.1","name":"myService","port":8081}
func (*Parser) MergeParser ¶
MergeParser applies other's internal document state to ours using bkl's merge semantics.
Example ¶
package main import ( "os" "github.com/gopatchy/bkl" ) func main() { b1 := bkl.New() b2 := bkl.New() if err := b1.MergeFileLayers("tests/tree/a.b.yaml"); err != nil { panic(err) } if err := b2.MergeFileLayers("tests/tree/c.d.yaml"); err != nil { panic(err) } err := b2.MergeParser(b1) if err != nil { panic(err) } if err = b2.OutputToWriter(os.Stdout, "json"); err != nil { panic(err) } }
Output: {"a":1,"b":2,"c":3,"d":4}
func (*Parser) MergePatch ¶
MergePatch applies the supplied patch to the Parser's current internal document state at the specified document index using bkl's merge semantics.
index is only a hint; if the patch contains a $match entry, that is used instead.
Example ¶
package main import ( "os" "github.com/gopatchy/bkl" ) func main() { b := bkl.New() err := b.MergePatch(0, map[string]any{"a": 1}) if err != nil { panic(err) } err = b.MergePatch(0, map[string]any{"b": 2}) if err != nil { panic(err) } if err = b.OutputToWriter(os.Stdout, "json"); err != nil { panic(err) } }
Output: {"a":1,"b":2}
func (*Parser) NumDocuments ¶
NumDocuments returns the number of documents in the Parser's internal state.
Example ¶
package main import ( "fmt" "github.com/gopatchy/bkl" ) func main() { b := bkl.New() if err := b.MergeFileLayers("tests/example1/service.test.toml"); err != nil { panic(err) } fmt.Println(b.NumDocuments()) }
Output: 1
func (*Parser) Output ¶
Output returns all documents encoded in the specified format and merged into a stream with ---.
Example ¶
package main import ( "os" "github.com/gopatchy/bkl" ) func main() { b := bkl.New() if err := b.MergeFileLayers("tests/output-multi/a.yaml"); err != nil { panic(err) } blob, err := b.Output("yaml") if err != nil { panic(err) } os.Stdout.Write(blob) }
Output: a: 1 b: 2 --- c: 3
func (*Parser) OutputIndex ¶
OutputIndex returns the outputs generated by the document at the specified index, encoded in the specified format.
Example ¶
package main import ( "os" "github.com/gopatchy/bkl" ) func main() { b := bkl.New() if err := b.MergeFileLayers("tests/stream-add/a.b.yaml"); err != nil { panic(err) } blobs, err := b.OutputIndex(1, "yaml") // second document if err != nil { panic(err) } os.Stdout.Write(blobs[0]) // first output of second document }
Output: c: 3
func (*Parser) OutputToFile ¶
OutputToFile encodes all documents in the specified format and writes them to the specified output path.
If format is "", it is inferred from path's file extension.
Example ¶
package main import ( "fmt" "io" "os" "github.com/gopatchy/bkl" ) func main() { b := bkl.New() if err := b.MergeFileLayers("tests/output-multi/a.yaml"); err != nil { panic(err) } f, err := os.CreateTemp("", "example") if err != nil { panic(err) } defer os.Remove(f.Name()) err = b.OutputToFile(f.Name(), "toml") if err != nil { panic(err) } blob, err := io.ReadAll(f) if err != nil { panic(err) } fmt.Println(string(blob)) }
Output: a = 1 b = 2 --- c = 3
func (*Parser) OutputToWriter ¶
OutputToWriter encodes all documents in the specified format and writes them to the specified io.Writer.
If format is "", it defaults to "json-pretty".
Example ¶
package main import ( "os" "github.com/gopatchy/bkl" ) func main() { b := bkl.New() if err := b.MergeFileLayers("tests/output-multi/a.yaml"); err != nil { panic(err) } err := b.OutputToWriter(os.Stdout, "yaml") if err != nil { panic(err) } }
Output: a: 1 b: 2 --- c: 3
func (*Parser) Outputs ¶
Outputs returns all outputs from all documents encoded in the specified format.
Example ¶
package main import ( "os" "github.com/gopatchy/bkl" ) func main() { b := bkl.New() if err := b.MergeFileLayers("tests/stream-add/a.b.yaml"); err != nil { panic(err) } blobs, err := b.Outputs("yaml") if err != nil { panic(err) } os.Stdout.Write(blobs[1]) // second overall output }
Output: c: 3
func (*Parser) SetDebug ¶
SetDebug enables or disables debug log output to stderr.
Example ¶
package main import ( "log" "os" "github.com/gopatchy/bkl" ) func main() { log.Default().SetFlags(0) log.Default().SetOutput(os.Stdout) b := bkl.New() b.SetDebug(true) if err := b.MergeFileLayers("tests/example1/service.test.toml"); err != nil { panic(err) } }
Output: [tests/example1/service.test.toml] loading [tests/example1/service.yaml] loading [tests/example1/service.yaml] merging [tests/example1/service.test.toml] merging