1package yamlpatch
2
3import (
4	"fmt"
5
6	yaml "gopkg.in/yaml.v2"
7)
8
9// Patch is an ordered collection of operations.
10type Patch []Operation
11
12// DecodePatch decodes the passed YAML document as if it were an RFC 6902 patch
13func DecodePatch(bs []byte) (Patch, error) {
14	var p Patch
15
16	err := yaml.Unmarshal(bs, &p)
17	if err != nil {
18		return nil, err
19	}
20
21	return p, nil
22}
23
24// Apply returns a YAML document that has been mutated per the patch
25func (p Patch) Apply(doc []byte) ([]byte, error) {
26	var iface interface{}
27	err := yaml.Unmarshal(doc, &iface)
28	if err != nil {
29		return nil, fmt.Errorf("failed unmarshaling doc: %s\n\n%s", string(doc), err)
30	}
31
32	var c Container
33	c = NewNode(&iface).Container()
34
35	for _, op := range p {
36		pathfinder := NewPathFinder(c)
37		if op.Path.ContainsExtendedSyntax() {
38			paths := pathfinder.Find(string(op.Path))
39			if paths == nil {
40				return nil, fmt.Errorf("could not expand pointer: %s", op.Path)
41			}
42
43			for _, path := range paths {
44				newOp := op
45				newOp.Path = OpPath(path)
46				err = newOp.Perform(c)
47				if err != nil {
48					return nil, err
49				}
50			}
51		} else {
52			err = op.Perform(c)
53			if err != nil {
54				return nil, err
55			}
56		}
57	}
58
59	return yaml.Marshal(c)
60}
61