diff --git a/api/converter/from_bytes.go b/api/converter/from_bytes.go index 3b60be6ab..872825caa 100644 --- a/api/converter/from_bytes.go +++ b/api/converter/from_bytes.go @@ -175,7 +175,6 @@ func fromJSONText( } rgaTreeSplit := crdt.NewRGATreeSplit(crdt.InitialTextNode()) - current := rgaTreeSplit.InitialHead() for _, pbNode := range pbText.Nodes { textNode, err := fromTextNode(pbNode) @@ -190,7 +189,7 @@ func fromJSONText( if insPrevID != nil { insPrevNode := rgaTreeSplit.FindNode(insPrevID) if insPrevNode == nil { - panic("insPrevNode should be presence") + return nil, fmt.Errorf("insPrevNode should be presence") } current.SetInsPrev(insPrevNode) } diff --git a/pkg/document/crdt/array.go b/pkg/document/crdt/array.go index df5be3e4e..e6f5cb918 100644 --- a/pkg/document/crdt/array.go +++ b/pkg/document/crdt/array.go @@ -95,16 +95,20 @@ func (a *Array) StructureAsString() string { } // DeepCopy copies itself deeply. -func (a *Array) DeepCopy() Element { +func (a *Array) DeepCopy() (Element, error) { elements := NewRGATreeList() for _, node := range a.elements.Nodes() { - elements.Add(node.elem.DeepCopy()) + copiedNode, err := node.elem.DeepCopy() + if err != nil { + return nil, err + } + elements.Add(copiedNode) } array := NewArray(elements, a.createdAt) array.removedAt = a.removedAt - return array + return array, nil } // CreatedAt returns the creation time of this array. diff --git a/pkg/document/crdt/counter.go b/pkg/document/crdt/counter.go index b31d57df6..0bf7c5159 100644 --- a/pkg/document/crdt/counter.go +++ b/pkg/document/crdt/counter.go @@ -96,9 +96,9 @@ func (p *Counter) Marshal() string { } // DeepCopy copies itself deeply. -func (p *Counter) DeepCopy() Element { +func (p *Counter) DeepCopy() (Element, error) { counter := *p - return &counter + return &counter, nil } // CreatedAt returns the creation time. diff --git a/pkg/document/crdt/element.go b/pkg/document/crdt/element.go index c915b8c8a..c2f155b1f 100644 --- a/pkg/document/crdt/element.go +++ b/pkg/document/crdt/element.go @@ -47,7 +47,7 @@ type Element interface { Marshal() string // DeepCopy copies itself deeply. - DeepCopy() Element + DeepCopy() (Element, error) // CreatedAt returns the creation time of this element. CreatedAt() *time.Ticket diff --git a/pkg/document/crdt/object.go b/pkg/document/crdt/object.go index f93fc84b3..2793261da 100644 --- a/pkg/document/crdt/object.go +++ b/pkg/document/crdt/object.go @@ -94,16 +94,20 @@ func (o *Object) Marshal() string { } // DeepCopy copies itself deeply. -func (o *Object) DeepCopy() Element { +func (o *Object) DeepCopy() (Element, error) { members := NewElementRHT() for _, node := range o.memberNodes.Nodes() { - members.Set(node.key, node.elem.DeepCopy()) + copiedNode, err := node.elem.DeepCopy() + if err != nil { + return nil, err + } + members.Set(node.key, copiedNode) } obj := NewObject(members, o.createdAt) obj.removedAt = o.removedAt - return obj + return obj, nil } // CreatedAt returns the creation time of this object. diff --git a/pkg/document/crdt/primitive.go b/pkg/document/crdt/primitive.go index 2bdc94b3f..0308db5c9 100644 --- a/pkg/document/crdt/primitive.go +++ b/pkg/document/crdt/primitive.go @@ -219,9 +219,9 @@ func (p *Primitive) Marshal() string { } // DeepCopy copies itself deeply. -func (p *Primitive) DeepCopy() Element { +func (p *Primitive) DeepCopy() (Element, error) { primitive := *p - return &primitive + return &primitive, nil } // CreatedAt returns the creation time. diff --git a/pkg/document/crdt/primitive_test.go b/pkg/document/crdt/primitive_test.go index ce5110f4a..b209875a2 100644 --- a/pkg/document/crdt/primitive_test.go +++ b/pkg/document/crdt/primitive_test.go @@ -53,7 +53,8 @@ func TestPrimitive(t *testing.T) { assert.Equal(t, prim.Value(), crdt.ValueFromBytes(prim.ValueType(), prim.Bytes())) assert.Equal(t, prim.Marshal(), test.marshal) - copied := prim.DeepCopy() + copied, err := prim.DeepCopy() + assert.NoError(t, err) assert.Equal(t, prim.CreatedAt(), copied.CreatedAt()) assert.Equal(t, prim.MovedAt(), copied.MovedAt()) assert.Equal(t, prim.Marshal(), copied.Marshal()) diff --git a/pkg/document/crdt/rga_tree_split.go b/pkg/document/crdt/rga_tree_split.go index 12a9aca32..582142635 100644 --- a/pkg/document/crdt/rga_tree_split.go +++ b/pkg/document/crdt/rga_tree_split.go @@ -43,7 +43,8 @@ func NewRGATreeSplitNodeID(createdAt *time.Ticket, offset int) *RGATreeSplitNode // Compare returns an integer comparing two ID. The result will be 0 if // id==other, -1 if id < other, and +1 if id > other. If the receiver or -// argument is nil, it would panic at runtime. +// argument is nil, it would panic at runtime. This method is following +// golang standard interface. func (id *RGATreeSplitNodeID) Compare(other llrb.Key) int { if id == nil || other == nil { panic("RGATreeSplitNodeID cannot be null") @@ -330,47 +331,53 @@ func (s *RGATreeSplit[V]) findNodePos(index int) *RGATreeSplitNodePos { func (s *RGATreeSplit[V]) findNodeWithSplit( pos *RGATreeSplitNodePos, updatedAt *time.Ticket, -) (*RGATreeSplitNode[V], *RGATreeSplitNode[V]) { +) (*RGATreeSplitNode[V], *RGATreeSplitNode[V], error) { absoluteID := pos.getAbsoluteID() - node := s.findFloorNodePreferToLeft(absoluteID) + node, err := s.findFloorNodePreferToLeft(absoluteID) + if err != nil { + return nil, nil, err + } relativeOffset := absoluteID.offset - node.id.offset - s.splitNode(node, relativeOffset) + _, err = s.splitNode(node, relativeOffset) + if err != nil { + return nil, nil, err + } for node.next != nil && node.next.createdAt().After(updatedAt) { node = node.next } - return node, node.next + return node, node.next, nil } -func (s *RGATreeSplit[V]) findFloorNodePreferToLeft(id *RGATreeSplitNodeID) *RGATreeSplitNode[V] { +func (s *RGATreeSplit[V]) findFloorNodePreferToLeft(id *RGATreeSplitNodeID) (*RGATreeSplitNode[V], error) { node := s.findFloorNode(id) if node == nil { - panic("the node of the given id should be found: " + s.StructureAsString()) + return nil, fmt.Errorf("the node of the given id should be found: " + s.StructureAsString()) } if id.offset > 0 && node.id.offset == id.offset { // NOTE: InsPrev may not be present due to GC. if node.insPrev == nil { - return node + return node, nil } node = node.insPrev } - return node + return node, nil } -func (s *RGATreeSplit[V]) splitNode(node *RGATreeSplitNode[V], offset int) *RGATreeSplitNode[V] { +func (s *RGATreeSplit[V]) splitNode(node *RGATreeSplitNode[V], offset int) (*RGATreeSplitNode[V], error) { if offset > node.contentLen() { - panic("offset should be less than or equal to length: " + s.StructureAsString()) + return nil, fmt.Errorf("offset should be less than or equal to length: " + s.StructureAsString()) } if offset == 0 { - return node + return node, nil } else if offset == node.contentLen() { - return node.next + return node.next, nil } splitNode := node.split(offset) @@ -383,7 +390,7 @@ func (s *RGATreeSplit[V]) splitNode(node *RGATreeSplitNode[V], offset int) *RGAT } splitNode.SetInsPrev(node) - return splitNode + return splitNode, nil } // InsertAfter inserts the given node after the given previous node. @@ -439,10 +446,16 @@ func (s *RGATreeSplit[V]) edit( latestCreatedAtMapByActor map[string]*time.Ticket, content V, editedAt *time.Ticket, -) (*RGATreeSplitNodePos, map[string]*time.Ticket) { +) (*RGATreeSplitNodePos, map[string]*time.Ticket, error) { // 01. Split nodes with from and to - toLeft, toRight := s.findNodeWithSplit(to, editedAt) - fromLeft, fromRight := s.findNodeWithSplit(from, editedAt) + toLeft, toRight, err := s.findNodeWithSplit(to, editedAt) + if err != nil { + return nil, nil, err + } + fromLeft, fromRight, err := s.findNodeWithSplit(from, editedAt) + if err != nil { + return nil, nil, err + } // 02. delete between from and to nodesToDelete := s.findBetween(fromRight, toRight) @@ -467,7 +480,7 @@ func (s *RGATreeSplit[V]) edit( s.removedNodeMap[key] = removedNode } - return caretPos, latestCreatedAtMap + return caretPos, latestCreatedAtMap, nil } func (s *RGATreeSplit[V]) findBetween(from, to *RGATreeSplitNode[V]) []*RGATreeSplitNode[V] { diff --git a/pkg/document/crdt/root.go b/pkg/document/crdt/root.go index 9d4454341..7aa7a2dd4 100644 --- a/pkg/document/crdt/root.go +++ b/pkg/document/crdt/root.go @@ -101,8 +101,12 @@ func (r *Root) RegisterTextElementWithGarbage(textType TextElement) { } // DeepCopy copies itself deeply. -func (r *Root) DeepCopy() *Root { - return NewRoot(r.object.DeepCopy().(*Object)) +func (r *Root) DeepCopy() (*Root, error) { + copiedObject, err := r.object.DeepCopy() + if err != nil { + return nil, err + } + return NewRoot(copiedObject.(*Object)), nil } // GarbageCollect purge elements that were removed before the given time. diff --git a/pkg/document/crdt/root_test.go b/pkg/document/crdt/root_test.go index f4d72f056..a78bca532 100644 --- a/pkg/document/crdt/root_test.go +++ b/pkg/document/crdt/root_test.go @@ -59,25 +59,29 @@ func TestRoot(t *testing.T) { text := crdt.NewText(crdt.NewRGATreeSplit(crdt.InitialTextNode()), ctx.IssueTimeTicket()) fromPos, toPos := text.CreateRange(0, 0) - text.Edit(fromPos, toPos, nil, "Hello World", nil, ctx.IssueTimeTicket()) + _, _, err := text.Edit(fromPos, toPos, nil, "Hello World", nil, ctx.IssueTimeTicket()) + assert.NoError(t, err) registerTextElementWithGarbage(fromPos, toPos, root, text) assert.Equal(t, "Hello World", text.String()) assert.Equal(t, 0, root.GarbageLen()) fromPos, toPos = text.CreateRange(5, 10) - text.Edit(fromPos, toPos, nil, "Yorkie", nil, ctx.IssueTimeTicket()) + _, _, err = text.Edit(fromPos, toPos, nil, "Yorkie", nil, ctx.IssueTimeTicket()) + assert.NoError(t, err) registerTextElementWithGarbage(fromPos, toPos, root, text) assert.Equal(t, "HelloYorkied", text.String()) assert.Equal(t, 1, root.GarbageLen()) fromPos, toPos = text.CreateRange(0, 5) - text.Edit(fromPos, toPos, nil, "", nil, ctx.IssueTimeTicket()) + _, _, err = text.Edit(fromPos, toPos, nil, "", nil, ctx.IssueTimeTicket()) + assert.NoError(t, err) registerTextElementWithGarbage(fromPos, toPos, root, text) assert.Equal(t, "Yorkied", text.String()) assert.Equal(t, 2, root.GarbageLen()) fromPos, toPos = text.CreateRange(6, 7) - text.Edit(fromPos, toPos, nil, "", nil, ctx.IssueTimeTicket()) + _, _, err = text.Edit(fromPos, toPos, nil, "", nil, ctx.IssueTimeTicket()) + assert.NoError(t, err) registerTextElementWithGarbage(fromPos, toPos, root, text) assert.Equal(t, "Yorkie", text.String()) assert.Equal(t, 3, root.GarbageLen()) @@ -115,7 +119,8 @@ func TestRoot(t *testing.T) { for _, tc := range tests { fromPos, toPos := text.CreateRange(tc.from, tc.to) - text.Edit(fromPos, toPos, nil, tc.content, nil, ctx.IssueTimeTicket()) + _, _, err := text.Edit(fromPos, toPos, nil, tc.content, nil, ctx.IssueTimeTicket()) + assert.NoError(t, err) registerTextElementWithGarbage(fromPos, toPos, root, text) assert.Equal(t, tc.want, text.String()) assert.Equal(t, tc.garbage, root.GarbageLen()) @@ -131,19 +136,22 @@ func TestRoot(t *testing.T) { text := crdt.NewText(crdt.NewRGATreeSplit(crdt.InitialTextNode()), ctx.IssueTimeTicket()) fromPos, toPos := text.CreateRange(0, 0) - text.Edit(fromPos, toPos, nil, "Hello World", nil, ctx.IssueTimeTicket()) + _, _, err := text.Edit(fromPos, toPos, nil, "Hello World", nil, ctx.IssueTimeTicket()) + assert.NoError(t, err) registerTextElementWithGarbage(fromPos, toPos, root, text) assert.Equal(t, `[{"val":"Hello World"}]`, text.Marshal()) assert.Equal(t, 0, root.GarbageLen()) fromPos, toPos = text.CreateRange(6, 11) - text.Edit(fromPos, toPos, nil, "Yorkie", nil, ctx.IssueTimeTicket()) + _, _, err = text.Edit(fromPos, toPos, nil, "Yorkie", nil, ctx.IssueTimeTicket()) + assert.NoError(t, err) registerTextElementWithGarbage(fromPos, toPos, root, text) assert.Equal(t, `[{"val":"Hello "},{"val":"Yorkie"}]`, text.Marshal()) assert.Equal(t, 1, root.GarbageLen()) fromPos, toPos = text.CreateRange(0, 6) - text.Edit(fromPos, toPos, nil, "", nil, ctx.IssueTimeTicket()) + _, _, err = text.Edit(fromPos, toPos, nil, "", nil, ctx.IssueTimeTicket()) + assert.NoError(t, err) registerTextElementWithGarbage(fromPos, toPos, root, text) assert.Equal(t, `[{"val":"Yorkie"}]`, text.Marshal()) assert.Equal(t, 2, root.GarbageLen()) diff --git a/pkg/document/crdt/text.go b/pkg/document/crdt/text.go index dac3f6e3d..4f235db56 100644 --- a/pkg/document/crdt/text.go +++ b/pkg/document/crdt/text.go @@ -166,23 +166,23 @@ func (t *Text) Marshal() string { } // DeepCopy copies itself deeply. -func (t *Text) DeepCopy() Element { +func (t *Text) DeepCopy() (Element, error) { rgaTreeSplit := NewRGATreeSplit(InitialTextNode()) - current := rgaTreeSplit.InitialHead() + for _, node := range t.Nodes() { current = rgaTreeSplit.InsertAfter(current, node.DeepCopy()) insPrevID := node.InsPrevID() if insPrevID != nil { insPrevNode := rgaTreeSplit.FindNode(insPrevID) if insPrevNode == nil { - panic("insPrevNode should be presence") + return nil, fmt.Errorf("insPrevNode should be presence") } current.SetInsPrev(insPrevNode) } } - return NewText(rgaTreeSplit, t.createdAt) + return NewText(rgaTreeSplit, t.createdAt), nil } // CreatedAt returns the creation time of this Text. @@ -233,21 +233,19 @@ func (t *Text) Edit( content string, attributes map[string]string, executedAt *time.Ticket, -) (*RGATreeSplitNodePos, map[string]*time.Ticket) { +) (*RGATreeSplitNodePos, map[string]*time.Ticket, error) { val := NewTextValue(content, NewRHT()) for key, value := range attributes { val.attrs.Set(key, value, executedAt) } - cursorPos, latestCreatedAtMapByActor := t.rgaTreeSplit.edit( + return t.rgaTreeSplit.edit( from, to, latestCreatedAtMapByActor, val, executedAt, ) - - return cursorPos, latestCreatedAtMapByActor } // Style applies the given attributes of the given range. @@ -256,10 +254,16 @@ func (t *Text) Style( to *RGATreeSplitNodePos, attributes map[string]string, executedAt *time.Ticket, -) { +) error { // 01. Split nodes with from and to - _, toRight := t.rgaTreeSplit.findNodeWithSplit(to, executedAt) - _, fromRight := t.rgaTreeSplit.findNodeWithSplit(from, executedAt) + _, toRight, err := t.rgaTreeSplit.findNodeWithSplit(to, executedAt) + if err != nil { + return err + } + _, fromRight, err := t.rgaTreeSplit.findNodeWithSplit(from, executedAt) + if err != nil { + return err + } // 02. style nodes between from and to nodes := t.rgaTreeSplit.findBetween(fromRight, toRight) @@ -269,6 +273,7 @@ func (t *Text) Style( val.attrs.Set(key, value, executedAt) } } + return nil } // Select stores that the given range has been selected. diff --git a/pkg/document/crdt/text_test.go b/pkg/document/crdt/text_test.go index 04fa873c9..92b4083ff 100644 --- a/pkg/document/crdt/text_test.go +++ b/pkg/document/crdt/text_test.go @@ -32,11 +32,13 @@ func TestText(t *testing.T) { text := crdt.NewText(crdt.NewRGATreeSplit(crdt.InitialTextNode()), ctx.IssueTimeTicket()) fromPos, toPos := text.CreateRange(0, 0) - text.Edit(fromPos, toPos, nil, "Hello World", nil, ctx.IssueTimeTicket()) + _, _, err := text.Edit(fromPos, toPos, nil, "Hello World", nil, ctx.IssueTimeTicket()) + assert.NoError(t, err) assert.Equal(t, `[{"val":"Hello World"}]`, text.Marshal()) fromPos, toPos = text.CreateRange(6, 11) - text.Edit(fromPos, toPos, nil, "Yorkie", nil, ctx.IssueTimeTicket()) + _, _, err = text.Edit(fromPos, toPos, nil, "Yorkie", nil, ctx.IssueTimeTicket()) + assert.NoError(t, err) assert.Equal(t, `[{"val":"Hello "},{"val":"Yorkie"}]`, text.Marshal()) }) @@ -65,19 +67,21 @@ func TestText(t *testing.T) { t.Run("marshal test", func(t *testing.T) { root := helper.TestRoot() ctx := helper.TextChangeContext(root) - text := crdt.NewText(crdt.NewRGATreeSplit(crdt.InitialTextNode()), ctx.IssueTimeTicket()) fromPos, toPos := text.CreateRange(0, 0) - text.Edit(fromPos, toPos, nil, "Hello World", nil, ctx.IssueTimeTicket()) + _, _, err := text.Edit(fromPos, toPos, nil, "Hello World", nil, ctx.IssueTimeTicket()) + assert.NoError(t, err) assert.Equal(t, `[{"val":"Hello World"}]`, text.Marshal()) fromPos, toPos = text.CreateRange(6, 11) - text.Edit(fromPos, toPos, nil, "Yorkie", nil, ctx.IssueTimeTicket()) + _, _, err = text.Edit(fromPos, toPos, nil, "Yorkie", nil, ctx.IssueTimeTicket()) + assert.NoError(t, err) assert.Equal(t, `[{"val":"Hello "},{"val":"Yorkie"}]`, text.Marshal()) fromPos, toPos = text.CreateRange(0, 1) - text.Style(fromPos, toPos, map[string]string{"b": "1"}, ctx.IssueTimeTicket()) + err = text.Style(fromPos, toPos, map[string]string{"b": "1"}, ctx.IssueTimeTicket()) + assert.NoError(t, err) assert.Equal( t, `[{"attrs":{"b":"1"},"val":"H"},{"val":"ello "},{"val":"Yorkie"}]`, diff --git a/pkg/document/document.go b/pkg/document/document.go index cc1bcbaf6..821a91269 100644 --- a/pkg/document/document.go +++ b/pkg/document/document.go @@ -59,7 +59,9 @@ func (d *Document) Update( return ErrDocumentRemoved } - d.ensureClone() + if err := d.ensureClone(); err != nil { + return err + } ctx := change.NewContext( d.doc.changeID.Next(), @@ -95,7 +97,9 @@ func (d *Document) ApplyChangePack(pack *change.Pack) error { return err } } else { - d.ensureClone() + if err := d.ensureClone(); err != nil { + return err + } for _, c := range pack.Changes { if err := c.Execute(d.clone); err != nil { @@ -194,7 +198,9 @@ func (d *Document) RootObject() *crdt.Object { // Root returns the root object of this document. func (d *Document) Root() *json.Object { - d.ensureClone() + if err := d.ensureClone(); err != nil { + panic(err) + } ctx := change.NewContext(d.doc.changeID.Next(), "", d.clone) return json.NewObject(ctx, d.clone.Object()) @@ -213,10 +219,15 @@ func (d *Document) GarbageLen() int { return d.doc.GarbageLen() } -func (d *Document) ensureClone() { +func (d *Document) ensureClone() error { if d.clone == nil { - d.clone = d.doc.root.DeepCopy() + copiedDoc, err := d.doc.root.DeepCopy() + if err != nil { + return err + } + d.clone = copiedDoc } + return nil } func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { diff --git a/pkg/document/json/array.go b/pkg/document/json/array.go index 1c0ba8e03..ab6f164fd 100644 --- a/pkg/document/json/array.go +++ b/pkg/document/json/array.go @@ -186,10 +186,14 @@ func (p *Array) insertAfterInternal( elem := creator(ticket) value := toOriginal(elem) + copiedValue, err := value.DeepCopy() + if err != nil { + panic(err) + } p.context.Push(operations.NewAdd( p.Array.CreatedAt(), prevCreatedAt, - value.DeepCopy(), + copiedValue, ticket, )) diff --git a/pkg/document/json/object.go b/pkg/document/json/object.go index c951d7fd1..8255fedbf 100644 --- a/pkg/document/json/object.go +++ b/pkg/document/json/object.go @@ -257,10 +257,14 @@ func (p *Object) setInternal( elem := creator(ticket) value := toOriginal(elem) + copiedValue, err := value.DeepCopy() + if err != nil { + panic(err) + } p.context.Push(operations.NewSet( p.CreatedAt(), k, - value.DeepCopy(), + copiedValue, ticket, )) diff --git a/pkg/document/json/text.go b/pkg/document/json/text.go index 4b4f656e0..f667a7e26 100644 --- a/pkg/document/json/text.go +++ b/pkg/document/json/text.go @@ -52,7 +52,7 @@ func (p *Text) Edit(from, to int, content string, attributes ...map[string]strin } ticket := p.context.IssueTimeTicket() - _, maxCreationMapByActor := p.Text.Edit( + _, maxCreationMapByActor, err := p.Text.Edit( fromPos, toPos, nil, @@ -60,6 +60,9 @@ func (p *Text) Edit(from, to int, content string, attributes ...map[string]strin attrs, ticket, ) + if err != nil { + panic(err) + } p.context.Push(operations.NewEdit( p.CreatedAt(), @@ -85,12 +88,14 @@ func (p *Text) Style(from, to int, attributes map[string]string) *Text { fromPos, toPos := p.Text.CreateRange(from, to) ticket := p.context.IssueTimeTicket() - p.Text.Style( + if err := p.Text.Style( fromPos, toPos, attributes, ticket, - ) + ); err != nil { + panic(err) + } p.context.Push(operations.NewStyle( p.CreatedAt(), diff --git a/pkg/document/operations/add.go b/pkg/document/operations/add.go index eb4be7aa9..d2e1ec58a 100644 --- a/pkg/document/operations/add.go +++ b/pkg/document/operations/add.go @@ -60,7 +60,10 @@ func (o *Add) Execute(root *crdt.Root) error { return ErrNotApplicableDataType } - value := o.value.DeepCopy() + value, err := o.value.DeepCopy() + if err != nil { + return err + } obj.InsertAfter(o.prevCreatedAt, value) root.RegisterElement(value) diff --git a/pkg/document/operations/edit.go b/pkg/document/operations/edit.go index 98cab3ed5..b1fd50baa 100644 --- a/pkg/document/operations/edit.go +++ b/pkg/document/operations/edit.go @@ -75,7 +75,10 @@ func (e *Edit) Execute(root *crdt.Root) error { switch obj := parent.(type) { case *crdt.Text: - obj.Edit(e.from, e.to, e.latestCreatedAtMapByActor, e.content, e.attributes, e.executedAt) + _, _, err := obj.Edit(e.from, e.to, e.latestCreatedAtMapByActor, e.content, e.attributes, e.executedAt) + if err != nil { + return err + } if !e.from.Equal(e.to) { root.RegisterTextElementWithGarbage(obj) } diff --git a/pkg/document/operations/set.go b/pkg/document/operations/set.go index 398bdd16e..2e2a66ce2 100644 --- a/pkg/document/operations/set.go +++ b/pkg/document/operations/set.go @@ -61,7 +61,10 @@ func (o *Set) Execute(root *crdt.Root) error { return ErrNotApplicableDataType } - value := o.value.DeepCopy() + value, err := o.value.DeepCopy() + if err != nil { + return err + } removed := obj.Set(o.key, value) root.RegisterElement(value) if removed != nil { diff --git a/pkg/document/operations/style.go b/pkg/document/operations/style.go index 46484c0f4..069f8a4d1 100644 --- a/pkg/document/operations/style.go +++ b/pkg/document/operations/style.go @@ -64,8 +64,7 @@ func (e *Style) Execute(root *crdt.Root) error { return ErrNotApplicableDataType } - obj.Style(e.from, e.to, e.attributes, e.executedAt) - return nil + return obj.Style(e.from, e.to, e.attributes, e.executedAt) } // From returns the start point of the editing range.