Files
kustomize/vendor/github.com/krishicks/yaml-patch/operation.go
2018-08-30 13:45:25 -07:00

182 lines
4.1 KiB
Go

package yamlpatch
import (
"errors"
"fmt"
"strings"
)
// Op is a type alias
type Op string
// Ops
const (
opAdd Op = "add"
opRemove Op = "remove"
opReplace Op = "replace"
opMove Op = "move"
opCopy Op = "copy"
opTest Op = "test"
)
// OpPath is an RFC6902 'pointer'
type OpPath string
// Decompose returns the pointer's components:
// "/foo" => [], "foo"
// "/foo/1" => ["foo"], "1"
// "/foo/1/bar" => ["foo", "1"], "bar"
func (p *OpPath) Decompose() ([]string, string, error) {
path := string(*p)
if !strings.HasPrefix(path, "/") {
return nil, "", fmt.Errorf("operation path is missing leading '/': %s", path)
}
parts := strings.Split(path, "/")[1:]
return parts[:len(parts)-1], parts[len(parts)-1], nil
}
// ContainsExtendedSyntax returns whether the OpPath uses the "key=value"
// format, as in "/foo/name=bar", where /foo points at an array that contains
// an object with a key "name" that has a value "bar"
func (p *OpPath) ContainsExtendedSyntax() bool {
return strings.Contains(string(*p), "=")
}
// String returns the OpPath as a string
func (p *OpPath) String() string {
return string(*p)
}
// Operation is an RFC6902 'Operation'
// https://tools.ietf.org/html/rfc6902#section-4
type Operation struct {
Op Op `yaml:"op,omitempty"`
Path OpPath `yaml:"path,omitempty"`
From OpPath `yaml:"from,omitempty"`
Value *Node `yaml:"value,omitempty"`
}
// Perform executes the operation on the given container
func (o *Operation) Perform(c Container) error {
var err error
switch o.Op {
case opAdd:
err = tryAdd(c, o)
case opRemove:
err = tryRemove(c, o)
case opReplace:
err = tryReplace(c, o)
case opMove:
err = tryMove(c, o)
case opCopy:
err = tryCopy(c, o)
case opTest:
err = tryTest(c, o)
default:
err = fmt.Errorf("Unexpected op: %s", o.Op)
}
return err
}
func tryAdd(doc Container, op *Operation) error {
con, key, err := findContainer(doc, &op.Path)
if err != nil {
return fmt.Errorf("yamlpatch add operation does not apply: doc is missing path: %s", op.Path)
}
return con.Add(key, op.Value)
}
func tryRemove(doc Container, op *Operation) error {
con, key, err := findContainer(doc, &op.Path)
if err != nil {
return fmt.Errorf("yamlpatch remove operation does not apply: doc is missing path: %s", op.Path)
}
return con.Remove(key)
}
func tryReplace(doc Container, op *Operation) error {
con, key, err := findContainer(doc, &op.Path)
if err != nil {
return fmt.Errorf("yamlpatch replace operation does not apply: doc is missing path: %s", op.Path)
}
val, err := con.Get(key)
if val == nil || err != nil {
return fmt.Errorf("yamlpatch replace operation does not apply: doc is missing key: %s", op.Path)
}
return con.Set(key, op.Value)
}
func tryMove(doc Container, op *Operation) error {
con, key, err := findContainer(doc, &op.From)
if err != nil {
return fmt.Errorf("yamlpatch move operation does not apply: doc is missing from path: %s", op.From)
}
val, err := con.Get(key)
if err != nil {
return err
}
err = con.Remove(key)
if err != nil {
return err
}
con, key, err = findContainer(doc, &op.Path)
if err != nil {
return fmt.Errorf("yamlpatch move operation does not apply: doc is missing destination path: %s", op.Path)
}
return con.Set(key, val)
}
func tryCopy(doc Container, op *Operation) error {
con, key, err := findContainer(doc, &op.From)
if err != nil {
return fmt.Errorf("copy operation does not apply: doc is missing from path: %s", op.From)
}
val, err := con.Get(key)
if err != nil {
return err
}
con, key, err = findContainer(doc, &op.Path)
if err != nil {
return fmt.Errorf("copy operation does not apply: doc is missing destination path: %s", op.Path)
}
return con.Set(key, val)
}
func tryTest(doc Container, op *Operation) error {
con, key, err := findContainer(doc, &op.Path)
if err != nil {
return fmt.Errorf("test operation does not apply: doc is missing from path: %s", op.From)
}
val, err := con.Get(key)
if err != nil {
return err
}
if op.Value.Empty() && val == nil {
return nil
}
if op.Value.Equal(val) {
return nil
}
return errors.New("test failed")
}