cborpatch

package module
v1.2.0-beta2 Latest Latest
Warning

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

Go to latest
Published: Jan 27, 2023 License: MIT Imports: 9 Imported by: 3

README

CBOR-Patch

CI Codecov CodeQL License Go Reference

cborpatch is a library which provides functionality for applying RFC6902 JSON patches on CBOR

cborpatch supports positive integer, negative integer, byte string and UTF-8 text string as map key.

Import

// package cborpatch
import "github.com/ldclabs/cbor-patch"

Examples

Create and apply a CBOR Patch
package main

import (
	"fmt"

	cborpatch "github.com/ldclabs/cbor-patch"
)

func main() {
	original := cborpatch.MustFromJSON(`{"name": "John", "age": 24, "height": 3.21}`)
	fmt.Printf("%x\n", original)
	// a3636167651818646e616d65644a6f686e66686569676874fb4009ae147ae147ae

	patch, err := cborpatch.PatchFromJSON(`[
		{"op": "replace", "path": "/name", "value": "Jane"},
		{"op": "remove", "path": "/height"}
	]`)
	if err != nil {
		panic(err)
	}
	modified, err := patch.Apply(original)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%x\n", modified)
	// a2636167651818646e616d65644a616e65
	fmt.Printf("%s\n", cborpatch.MustToJSON(modified))
	// {"age":24,"name":"Jane"}
}
Create a Node and apply more Patchs
package main

import (
	"fmt"

	cborpatch "github.com/ldclabs/cbor-patch"
)

func main() {
	original := cborpatch.MustFromJSON(`{"name": "John", "age": 24, "height": 3.21}`)
	fmt.Printf("%x\n", original)
	// a3636167651818646e616d65644a6f686e66686569676874fb4009ae147ae147ae

	node := cborpatch.NewNode(original)
	patch, err := cborpatch.PatchFromJSON(`[
		{"op": "replace", "path": "/name", "value": "Jane"},
		{"op": "remove", "path": "/height"}
	]`)
	if err != nil {
		panic(err)
	}
	err = node.Patch(patch, nil)
	if err != nil {
		panic(err)
	}
	modified, err := node.MarshalCBOR()
	if err != nil {
		panic(err)
	}
	fmt.Printf("%x\n", modified)
	// a2636167651818646e616d65644a616e65
	modified, err = node.MarshalJSON()
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", string(modified))
	// {"age":24,"name":"Jane"}

	patch, err = cborpatch.PatchFromJSON(`[
		{"op": "replace", "path": "/age", "value": 25}
	]`)
	if err != nil {
		panic(err)
	}
	err = node.Patch(patch, nil)
	if err != nil {
		panic(err)
	}
	modified, err = node.MarshalCBOR()
	if err != nil {
		panic(err)
	}
	fmt.Printf("%x\n", modified)
	// a2636167651819646e616d65644a616e65
	modified, err = node.MarshalJSON()
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", string(modified))
	// {"age":25,"name":"Jane"}
}
Get value by path
package main

import (
	"fmt"

	cborpatch "github.com/ldclabs/cbor-patch"
)

func main() {
	doc := cborpatch.MustFromJSON(`{
		"baz": "qux",
		"foo": [ "a", 2, "c" ]
	}`)
	node := cborpatch.NewNode(doc)
	path, err := cborpatch.PathFromJSON("/foo/0")
	if err != nil {
		panic(err)
	}

	value, err := node.GetValue(path, nil)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%x\n", value)
	// 6161
	fmt.Printf("%s\n", cborpatch.MustToJSON(value))
	// "a"
}
Find children by test operations
package main

import (
	"fmt"

	cborpatch "github.com/ldclabs/cbor-patch"
)

func main() {
	doc := cborpatch.MustFromJSON(`["root", ["p",
		["span", {"data-type": "text"},
			["span", {"data-type": "leaf"}, "Hello 1"],
			["span", {"data-type": "leaf"}, "Hello 2"],
			["span", {"data-type": "leaf"}, "Hello 3"],
			["span", {"data-type": null}, "Hello 4"]
		]
	]]`)

	node := cborpatch.NewNode(doc)
	tests := cborpatch.PVs{
		{cborpatch.PathMustFromJSON("/0"), cborpatch.MustFromJSON(`"span"`)},
		{cborpatch.PathMustFromJSON("/1/data-type"), cborpatch.MustFromJSON(`"leaf"`)},
	}

	result, err := node.FindChildren(tests, nil)
	if err != nil {
		panic(err)
	}
	for _, r := range result {
		fmt.Printf("Path: %s, Value: %x, JSON: %s\n", r.Path, r.Value, cborpatch.MustToJSON(r.Value))
	}

	// Output:
	// Path: [1, 1, 2], Value: 83647370616ea169646174612d74797065646c6561666748656c6c6f2031, JSON: ["span",{"data-type":"leaf"},"Hello 1"]
	// Path: [1, 1, 3], Value: 83647370616ea169646174612d74797065646c6561666748656c6c6f2032, JSON: ["span",{"data-type":"leaf"},"Hello 2"]
	// Path: [1, 1, 4], Value: 83647370616ea169646174612d74797065646c6561666748656c6c6f2033, JSON: ["span",{"data-type":"leaf"},"Hello 3"]
}

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// SupportNegativeIndices decides whether to support non-standard practice of
	// allowing negative indices to mean indices starting at the end of an array.
	// Default to true.
	SupportNegativeIndices bool = true
	// AccumulatedCopySizeLimit limits the total size increase in bytes caused by
	// "copy" operations in a patch.
	AccumulatedCopySizeLimit int64 = 0
)
View Source
var (
	ErrMissing      = errors.New("missing value")
	ErrUnknownType  = errors.New("unknown object type")
	ErrInvalid      = errors.New("invalid node detected")
	ErrInvalidIndex = errors.New("invalid index referenced")
)

Functions

func Diagify

func Diagify(doc []byte) string

Diagify returns the doc as CBOR diagnostic notation. If the doc is a invalid CBOR bytes, it returns the doc with base16 encoding like a byte string.

func Equal

func Equal(a, b []byte) bool

Equal indicates if 2 CBOR documents have the same structural equality.

func FromJSON

func FromJSON(doc []byte, v any) ([]byte, error)

FromJSON converts a JSON-encoded data to a CBOR-encoded data with a optional value as struct container. If v is not nil, it will decode data into v and then encode v to CBOR-encoded data. If v is nil, it will decode data with the following rules:

JSON booleans decode to bool.
JSON positive integers decode to uint64 (big.Int if value overflows).
JSON negative integers decode to int64 (big.Int if value overflows).
JSON floating points decode to float64.
JSON text strings decode to string.
JSON arrays decode to []any.
JSON objects decode to map[string]any.
JSON null decode to nil.

func GetValueByPath

func GetValueByPath(doc []byte, path Path) ([]byte, error)

GetValueByPath returns the value of a given path in a raw encoded CBOR document.

func MustFromJSON

func MustFromJSON(doc string) []byte

MustFromJSON converts a JSON-encoded string to a CBOR-encoded data. It will panic if converting failed.

func MustMarshal

func MustMarshal(val any) []byte

func MustToJSON

func MustToJSON(doc []byte) string

MustToJSON converts a CBOR-encoded data to a JSON-encoded string. It will panic if converting failed.

func SetCBOR

func SetCBOR(
	marshal func(v any) ([]byte, error),
	unmarshal func(data []byte, v any) error,
)

SetCBOR set the underlying global CBOR Marshal and Unmarshal functions.

func init() {
	var EncMode, _ = cbor.CanonicalEncOptions().EncMode()
	var DecMode, _ = cbor.DecOptions{
		DupMapKey:   cbor.DupMapKeyQuiet,
		IndefLength: cbor.IndefLengthForbidden,
	}.DecMode()

	cborpatch.SetCBOR(EncMode.Marshal, DecMode.Unmarshal)
}

func ToJSON

func ToJSON(doc []byte, v any) ([]byte, error)

ToJSON converts a CBOR-encoded data to a JSON-encoded data with a optional value as struct container. If v is not nil, it will decode data into v and then encode v to JSON-encoded data.

Types

type AccumulatedCopySizeError

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

AccumulatedCopySizeError is an error type returned when the accumulated size increase caused by copy operations in a patch operation has exceeded the limit.

func NewAccumulatedCopySizeError

func NewAccumulatedCopySizeError(l, a int64) *AccumulatedCopySizeError

NewAccumulatedCopySizeError returns an AccumulatedCopySizeError.

func (*AccumulatedCopySizeError) Error

func (a *AccumulatedCopySizeError) Error() string

Error implements the error interface.

type ByteString

type ByteString = cbor.ByteString

type CBORType

type CBORType uint8

CBORType is the type of a raw encoded CBOR value.

const (
	CBORTypePositiveInt CBORType = 0x00
	CBORTypeNegativeInt CBORType = 0x20
	CBORTypeByteString  CBORType = 0x40
	CBORTypeTextString  CBORType = 0x60
	CBORTypeArray       CBORType = 0x80
	CBORTypeMap         CBORType = 0xa0
	CBORTypeTag         CBORType = 0xc0
	CBORTypePrimitives  CBORType = 0xe0
	CBORTypeInvalid     CBORType = 0xff
)

Predefined CBORTypes.

func ReadCBORType

func ReadCBORType(data []byte) CBORType

ReadCBORType returns the type of a raw encoded CBOR value.

func (CBORType) String

func (t CBORType) String() string

String returns a string representation of CBORType.

type Node

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

Node represents a lazy parsing CBOR document.

func NewNode

func NewNode(doc RawMessage) *Node

NewNode returns a new Node with the given raw encoded CBOR document. A nil or empty raw document is equal to CBOR null.

func (*Node) Equal added in v1.1.0

func (n *Node) Equal(o *Node) bool

Equal indicates if two CBOR Nodes have the same structural equality.

func (*Node) FindChildren

func (n *Node) FindChildren(tests []*PV, options *Options) (result []*PV, err error)

FindChildren returns the children nodes that pass the given tests in the node.

Example
package main

import (
	"fmt"

	cborpatch "github.com/ldclabs/cbor-patch"
)

func main() {
	doc := cborpatch.MustFromJSON(`["root", ["p",
		["span", {"data-type": "text"},
			["span", {"data-type": "leaf"}, "Hello 1"],
			["span", {"data-type": "leaf"}, "Hello 2"],
			["span", {"data-type": "leaf"}, "Hello 3"],
			["span", {"data-type": null}, "Hello 4"]
		]
	]]`)

	node := cborpatch.NewNode(doc)
	tests := cborpatch.PVs{
		{cborpatch.PathMustFromJSON("/0"), cborpatch.MustFromJSON(`"span"`)},
		{cborpatch.PathMustFromJSON("/1/data-type"), cborpatch.MustFromJSON(`"leaf"`)},
	}

	result, err := node.FindChildren(tests, nil)
	if err != nil {
		panic(err)
	}
	for _, r := range result {
		fmt.Printf("Path: %s, Value: %x, JSON: %s\n", r.Path, r.Value, cborpatch.MustToJSON(r.Value))
	}

}
Output:

Path: [1, 1, 2], Value: 83647370616ea169646174612d74797065646c6561666748656c6c6f2031, JSON: ["span",{"data-type":"leaf"},"Hello 1"]
Path: [1, 1, 3], Value: 83647370616ea169646174612d74797065646c6561666748656c6c6f2032, JSON: ["span",{"data-type":"leaf"},"Hello 2"]
Path: [1, 1, 4], Value: 83647370616ea169646174612d74797065646c6561666748656c6c6f2033, JSON: ["span",{"data-type":"leaf"},"Hello 3"]

func (*Node) GetChild

func (n *Node) GetChild(path Path, options *Options) (*Node, error)

GetChild returns the child node of a given path in the node.

func (*Node) GetValue

func (n *Node) GetValue(path Path, options *Options) (RawMessage, error)

GetValue returns the child node of a given path in the node.

Example
package main

import (
	"fmt"

	cborpatch "github.com/ldclabs/cbor-patch"
)

func main() {
	doc := cborpatch.MustFromJSON(`{
		"baz": "qux",
		"foo": [ "a", 2, "c" ]
	}`)
	node := cborpatch.NewNode(doc)
	path, err := cborpatch.PathFromJSON("/foo/0")
	if err != nil {
		panic(err)
	}

	value, err := node.GetValue(path, nil)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%x\n", value)
	// 6161
	fmt.Printf("%s\n", cborpatch.MustToJSON(value))
	// "a"

}
Output:

6161
"a"

func (*Node) MarshalCBOR

func (n *Node) MarshalCBOR() ([]byte, error)

MarshalCBOR implements the cbor.Marshaler interface.

func (*Node) MarshalJSON

func (n *Node) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

func (*Node) Patch

func (n *Node) Patch(p Patch, options *Options) error

Patch applies the given patch to the node. It only supports string keys in a map node.

Example
package main

import (
	"fmt"

	cborpatch "github.com/ldclabs/cbor-patch"
)

func main() {
	original := cborpatch.MustFromJSON(`{"name": "John", "age": 24, "height": 3.21}`)
	fmt.Printf("%x\n", original)
	// a3636167651818646e616d65644a6f686e66686569676874fb4009ae147ae147ae

	node := cborpatch.NewNode(original)
	patch, err := cborpatch.PatchFromJSON(`[
		{"op": "replace", "path": "/name", "value": "Jane"},
		{"op": "remove", "path": "/height"}
	]`)
	if err != nil {
		panic(err)
	}
	err = node.Patch(patch, nil)
	if err != nil {
		panic(err)
	}
	modified, err := node.MarshalCBOR()
	if err != nil {
		panic(err)
	}
	fmt.Printf("%x\n", modified)
	// a2636167651818646e616d65644a616e65
	modified, err = node.MarshalJSON()
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", string(modified))
	// {"age":24,"name":"Jane"}

	patch, err = cborpatch.PatchFromJSON(`[
		{"op": "replace", "path": "/age", "value": 25}
	]`)
	if err != nil {
		panic(err)
	}
	err = node.Patch(patch, nil)
	if err != nil {
		panic(err)
	}
	modified, err = node.MarshalCBOR()
	if err != nil {
		panic(err)
	}
	fmt.Printf("%x\n", modified)
	// a2636167651819646e616d65644a616e65
	modified, err = node.MarshalJSON()
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", string(modified))
	// {"age":25,"name":"Jane"}

}
Output:

a3636167651818646e616d65644a6f686e66686569676874fb4009ae147ae147ae
a2636167651818646e616d65644a616e65
{"age":24,"name":"Jane"}
a2636167651819646e616d65644a616e65
{"age":25,"name":"Jane"}

func (*Node) String added in v1.1.1

func (n *Node) String() string

String returns the Node as CBOR diagnostic notation.

func (*Node) UnmarshalCBOR

func (n *Node) UnmarshalCBOR(data []byte) error

UnmarshalCBOR implements the cbor.Unmarshaler interface.

type Op

type Op int
const (
	OpReserved Op = iota
	OpAdd
	OpRemove
	OpReplace
	OpMove
	OpCopy
	OpTest
)

func (Op) Operation

func (op Op) Operation(from, path []any, value any) (*Operation, error)

func (Op) String

func (op Op) String() string

String returns a string representation of the Op.

type Operation

type Operation struct {
	Op    Op         `cbor:"1,keyasint"`
	From  Path       `cbor:"2,keyasint,omitempty"`
	Path  Path       `cbor:"3,keyasint"`
	Value RawMessage `cbor:"4,keyasint,omitempty"`
}

Operation is a single CBOR-Patch step, such as a single 'add' operation.

func (*Operation) Valid

func (o *Operation) Valid() error

type Options

type Options struct {
	// SupportNegativeIndices decides whether to support non-standard practice of
	// allowing negative indices to mean indices starting at the end of an array.
	// Default to true.
	SupportNegativeIndices bool
	// AccumulatedCopySizeLimit limits the total size increase in bytes caused by
	// "copy" operations in a patch.
	AccumulatedCopySizeLimit int64
	// AllowMissingPathOnRemove indicates whether to fail "remove" operations when the target path is missing.
	// Default to false.
	AllowMissingPathOnRemove bool
	// EnsurePathExistsOnAdd instructs cbor-patch to recursively create the missing parts of path on "add" operation.
	// Default to false.
	EnsurePathExistsOnAdd bool
}

Options specifies options for calls to ApplyWithOptions. Use NewOptions to obtain default values for Options.

func NewOptions

func NewOptions() *Options

NewOptions creates a default set of options for calls to ApplyWithOptions.

type PV

type PV struct {
	Path  Path       `cbor:"3,keyasint,omitempty"`
	Value RawMessage `cbor:"4,keyasint,omitempty"`
}

PV represents a node with a path and a raw encoded CBOR value.

type PVs added in v1.0.1

type PVs []*PV

PVs represents a list of PV.

type Patch

type Patch []*Operation

Patch is an ordered collection of Operations.

func NewPatch added in v1.0.1

func NewPatch(doc []byte) (Patch, error)

NewPatch decodes the passed CBOR document as an RFC 6902 patch.

func PatchFromJSON

func PatchFromJSON(jsonpatch string) (Patch, error)

func (Patch) Apply

func (p Patch) Apply(doc []byte) ([]byte, error)

Apply mutates a CBOR document according to the patch, and returns the new document.

Example
package main

import (
	"fmt"

	cborpatch "github.com/ldclabs/cbor-patch"
)

func main() {
	original := cborpatch.MustFromJSON(`{"name": "John", "age": 24, "height": 3.21}`)
	fmt.Printf("%x\n", original)
	// a3636167651818646e616d65644a6f686e66686569676874fb4009ae147ae147ae

	patch, err := cborpatch.PatchFromJSON(`[
		{"op": "replace", "path": "/name", "value": "Jane"},
		{"op": "remove", "path": "/height"}
	]`)
	if err != nil {
		panic(err)
	}
	modified, err := patch.Apply(original)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%x\n", modified)
	// a2636167651818646e616d65644a616e65
	fmt.Printf("%s\n", cborpatch.MustToJSON(modified))
	// {"age":24,"name":"Jane"}

}
Output:

a3636167651818646e616d65644a6f686e66686569676874fb4009ae147ae147ae
a2636167651818646e616d65644a616e65
{"age":24,"name":"Jane"}

func (Patch) ApplyWithOptions

func (p Patch) ApplyWithOptions(doc []byte, options *Options) ([]byte, error)

ApplyWithOptions mutates a CBOR document according to the patch and the passed in Options. It returns the new document.

func (Patch) Valid

func (p Patch) Valid() error

type Path

type Path []RawKey

func PathFrom

func PathFrom(keys ...any) (Path, error)

func PathFromJSON

func PathFromJSON(jsonpath string) (Path, error)

func PathMustFrom

func PathMustFrom(keys ...any) Path

func PathMustFromJSON

func PathMustFromJSON(jsonpath string) Path

func (Path) String

func (p Path) String() string

String returns the Path as CBOR diagnostic notation.

func (Path) WithKey

func (p Path) WithKey(key RawKey) Path

type RawKey

type RawKey string

rawKey is a raw encoded CBOR value for map key.

func (RawKey) Bytes

func (k RawKey) Bytes() []byte

func (RawKey) Equal

func (k RawKey) Equal(other RawKey) bool

func (RawKey) Is

func (k RawKey) Is(other any) bool

func (RawKey) Key

func (k RawKey) Key() string

Key returns a string notation as JSON Object key.

func (RawKey) MarshalCBOR

func (k RawKey) MarshalCBOR() ([]byte, error)

MarshalCBOR returns m or CBOR nil if m is nil.

func (RawKey) String

func (k RawKey) String() string

String returns the rawKey as CBOR diagnostic notation.

func (*RawKey) UnmarshalCBOR

func (k *RawKey) UnmarshalCBOR(data []byte) error

UnmarshalCBOR creates a copy of data and saves to *k.

func (RawKey) Valid

func (k RawKey) Valid() error

type RawMessage

type RawMessage = cbor.RawMessage

RawMessage is a raw encoded CBOR value.

Jump to

Keyboard shortcuts

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