yamled

package module
v0.5.1 Latest Latest
Warning

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

Go to latest
Published: Jun 20, 2023 License: MIT Imports: 6 Imported by: 0

README

yamled

last stable release go report card godoc

yamled is a slim Go library that allows you to edit a YAML document (parsed by yaml.v3) in-memory. Compared to unmarshaling/marshaling data into structs, this approach has the advantage of keeping comments and formatting (mostly) intact.

Installation

go get go.xrstf.de/yamled

Usage

Unmarshalling

Marshalling and unmarshalling works just as you've done it before. Use yaml.v3 and, importantly, decode into a yaml.Node data stucture:

import yaml "gopkg.in/yaml.v3"

myDocument := strings.TrimSpace(`
hello: world
thisis: cool
`)

var node yaml.Node
if err := yaml.NewDecoder(strings.NewReader(myDocument)).Decode(&node); err != nil {
   log.Fatalf("Failed to decode YAML: %v", err)
}
Using

Once you have a yaml.Node, wrap it in a yamled.Document (this is cheap and quick):

import yaml "gopkg.in/yaml.v3"
import "go.xrstf.de/yamled"

doc, err := yamled.NewDocument(node)
if err != nil {
   log.Fatalf("Could not wrap node: %v", err) // most likely you used a non-document node
}

This Document instance now allows you to manage the document in memory. You can have many different wrappers around the same yaml.Node, but yamled is not concurrency safe, so make sure only a single goroutine modifies a document at a time.

Check the API documentation for the available functions. For example you can get a value from a deeply nested structure like so:

node, exists := doc.Get("key", 0, "subkey", "settings", "firstname")
if !exists {
   log.Fatal("There is no path to key.0.subkey.settings.firstname")
}

fmt.Println(node.ToString()) // could print "Thomas"
Marshalling

Important: You cannot yaml.Marshal() a yamled.Document object. yaml.v3 is hardcoded to only support document-wide settings (like the head comment) only if it encounters a well-known yaml.Node. Trying to marshal a document would result in a half-broken YAML and that's why there is a panic() built in.

Instead, use the helper functions .Bytes(indent) and .Encode(encoder) to turn your document back into YAML.

encoded, err := doc.Bytes(2)
if err != nil {
   log.Fatal("Failed to encode document as YAML: %v", err)
}

fmt.Println(string(encoded))

License

MIT

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func KindName

func KindName(k yaml.Kind) string

func StyleName

func StyleName(s yaml.Style) string

Types

type Document

type Document interface {
	// Documents *cannot* be marshalled by just throwing them into
	// a YAML encoder. The yaml.v3 encoder is hard-wired to expect
	// a specific type for the document node and since yaml.Document
	// is a wrapper, yaml.v3 will ignore the HeadComment as it is
	// not recognized as a document.
	// Document implements this interface to issue a panic whenever
	// an accidental attempt is made to encode it.
	// Use Bytes() or Encode() instead.
	// This limitation does not apply to yamled.Node objects.
	yaml.Marshaler

	Bytes(indent int) ([]byte, error)
	Encode(encoder *yaml.Encoder) error

	RootNode() (Node, error)
	Get(steps ...Step) (Node, bool)
	GetKey(steps ...Step) (KeyNode, bool)
	MustGet(steps ...Step) Node
	Set(value interface{}) error
	SetKey(key Step, value interface{}) (Node, error)
	SetAt(path Path, value interface{}) (Node, error)

	Replace(value interface{}) error
	ReplaceKey(key Step, value interface{}) (Node, error)
	ReplaceAt(path Path, value interface{}) (Node, error)

	DeleteKey(steps ...Step) error

	ToSlice() []interface{}
	ToMap() map[string]interface{}
	To(val interface{}) error

	HeadComment() string
	LineComment() string
	FootComment() string

	SetHeadComment(comment string) Document
	SetLineComment(comment string) Document
	SetFootComment(comment string) Document
}

func NewDocument

func NewDocument(n *yaml.Node) (Document, error)

type KeyNode added in v0.3.0

type KeyNode interface {
	fmt.Stringer

	HeadComment() string
	LineComment() string
	FootComment() string

	SetHeadComment(comment string) KeyNode
	SetLineComment(comment string) KeyNode
	SetFootComment(comment string) KeyNode
}

KeyNode is a specialized interface that makes setting comments for mapping keys more useful, as setting a comment on the value node itself might be undesirable.

type Node

type Node interface {
	yaml.Marshaler
	fmt.Stringer

	Bytes(indent int) ([]byte, error)
	Encode(encoder *yaml.Encoder) error

	Kind() yaml.Kind

	Style() yaml.Style
	SetStyle(style yaml.Style) error

	Get(steps ...Step) (Node, bool)
	GetKey(steps ...Step) (KeyNode, bool)
	MustGet(steps ...Step) Node
	Set(value interface{}) error
	SetKey(key Step, value interface{}) (Node, error)
	SetAt(path Path, value interface{}) (Node, error)

	Replace(value interface{}) error
	ReplaceKey(key Step, value interface{}) (Node, error)
	ReplaceAt(path Path, value interface{}) (Node, error)

	DeleteKey(steps ...Step) error

	ToString() string
	ToInt() int
	ToBool() bool
	ToSlice() []interface{}
	ToMap() map[string]interface{}
	To(val interface{}) error

	HeadComment() string
	LineComment() string
	FootComment() string

	SetHeadComment(comment string) Node
	SetLineComment(comment string) Node
	SetFootComment(comment string) Node
}

func NewNode

func NewNode(n *yaml.Node) (Node, error)

func NewNodeFromReader

func NewNodeFromReader(r io.Reader) (Node, error)

type Path

type Path []Step

func (Path) Append

func (p Path) Append(s ...Step) Path

func (Path) Consume

func (p Path) Consume() (Step, Path)

func (Path) End

func (p Path) End() Step

func (Path) Parent

func (p Path) Parent() Path

Parent returns the path except for the last element.

func (Path) Prepend

func (p Path) Prepend(s ...Step) Path

func (Path) Start

func (p Path) Start() Step

func (Path) String

func (p Path) String() string

func (Path) Validate

func (p Path) Validate() error

type Step

type Step interface{}

Jump to

Keyboard shortcuts

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