cborpatch

package module
v1.1.3 Latest Latest
Warning

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

Go to latest
Published: Jul 5, 2022 License: MIT Imports: 9 Imported by: 3

README

CBOR-Patch

ci codecov

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

Documentation

Go-Documentation

CBOR-Patch only supports string keys in a map.

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
	patchDoc := cborpatch.MustFromJSON(`[
		{"op": "replace", "path": "/name", "value": "Jane"},
		{"op": "remove", "path": "/height"}
	]`)

	patch, err := cborpatch.NewPatch(patchDoc)
	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
	patchDoc0 := cborpatch.MustFromJSON(`[
		{"op": "replace", "path": "/name", "value": "Jane"},
		{"op": "remove", "path": "/height"}
	]`)
	patchDoc1 := cborpatch.MustFromJSON(`[
		{"op": "replace", "path": "/age", "value": 25}
	]`)

	node := cborpatch.NewNode(original)
	patch, err := cborpatch.NewPatch(patchDoc0)
	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.NewPatch(patchDoc1)
	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)

	value, err := node.GetValue("/foo/0", 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{
		{"/0", cborpatch.MustFromJSON(`"span"`)},
		{"/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 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 interface{}) ([]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 []interface{}.
JSON objects decode to map[string]interface{}.
JSON null decode to nil.

func GetValueByPath

func GetValueByPath(doc []byte, path string) ([]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 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 interface{}) ([]byte, error),
	unmarshal func(data []byte, v interface{}) error,
)

SetCBOR set the underlying global CBOR Marshal and Unmarshal functions. The default is cbor.CanonicalEncOptions's Marshal and default cbor.Unmarshal.

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 interface{}) ([]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 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 2 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
doc := 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 := NewNode(doc)
tests := PVs{
	{"/0", MustFromJSON(`"span"`)},
	{"/1/data-type", 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, 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 string, options *Options) (*Node, error)

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

func (*Node) GetValue

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

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

Example
doc := MustFromJSON(`{
		"baz": "qux",
		"foo": [ "a", 2, "c" ]
	}`)
node := NewNode(doc)

value, err := node.GetValue("/foo/0", nil)
if err != nil {
	panic(err)
}
fmt.Printf("%x\n", value)
// 6161
fmt.Printf("%s\n", 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
original := MustFromJSON(`{"name": "John", "age": 24, "height": 3.21}`)
fmt.Printf("%x\n", original)
// a3636167651818646e616d65644a6f686e66686569676874fb4009ae147ae147ae
patchDoc0 := MustFromJSON(`[
		{"op": "replace", "path": "/name", "value": "Jane"},
		{"op": "remove", "path": "/height"}
	]`)
patchDoc1 := MustFromJSON(`[
		{"op": "replace", "path": "/age", "value": 25}
	]`)

node := NewNode(original)
patch, err := NewPatch(patchDoc0)
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 = NewPatch(patchDoc1)
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 a string representation of the node.

func (*Node) UnmarshalCBOR

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

UnmarshalCBOR implements the cbor.Unmarshaler interface.

type Operation

type Operation struct {
	Op    string     `cbor:"op"`
	Path  string     `cbor:"path"`
	From  string     `cbor:"from,omitempty"`
	Value RawMessage `cbor:"value,omitempty"`
}

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

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  string     `cbor:"path"`
	Value RawMessage `cbor:"value"`
}

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 (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
original := MustFromJSON(`{"name": "John", "age": 24, "height": 3.21}`)
fmt.Printf("%x\n", original)
// a3636167651818646e616d65644a6f686e66686569676874fb4009ae147ae147ae
patchDoc := MustFromJSON(`[
		{"op": "replace", "path": "/name", "value": "Jane"},
		{"op": "remove", "path": "/height"}
	]`)

patch, err := NewPatch(patchDoc)
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", 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.

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