1// Copyright 2022 The Go Authors. All rights reserved.
 2// Use of this source code is governed by a BSD-style
 3// license that can be found in the LICENSE file.
 4
 5package protocol
 6
 7import (
 8	"encoding/json"
 9	"fmt"
10)
11
12// DocumentChange is a union of various file edit operations.
13//
14// Exactly one field of this struct is non-nil; see [DocumentChange.Valid].
15//
16// See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#resourceChanges
17type DocumentChange struct {
18	TextDocumentEdit *TextDocumentEdit
19	CreateFile       *CreateFile
20	RenameFile       *RenameFile
21	DeleteFile       *DeleteFile
22}
23
24// Valid reports whether the DocumentChange sum-type value is valid,
25// that is, exactly one of create, delete, edit, or rename.
26func (d DocumentChange) Valid() bool {
27	n := 0
28	if d.TextDocumentEdit != nil {
29		n++
30	}
31	if d.CreateFile != nil {
32		n++
33	}
34	if d.RenameFile != nil {
35		n++
36	}
37	if d.DeleteFile != nil {
38		n++
39	}
40	return n == 1
41}
42
43func (d *DocumentChange) UnmarshalJSON(data []byte) error {
44	var m map[string]any
45	if err := json.Unmarshal(data, &m); err != nil {
46		return err
47	}
48
49	if _, ok := m["textDocument"]; ok {
50		d.TextDocumentEdit = new(TextDocumentEdit)
51		return json.Unmarshal(data, d.TextDocumentEdit)
52	}
53
54	// The {Create,Rename,Delete}File types all share a 'kind' field.
55	kind := m["kind"]
56	switch kind {
57	case "create":
58		d.CreateFile = new(CreateFile)
59		return json.Unmarshal(data, d.CreateFile)
60	case "rename":
61		d.RenameFile = new(RenameFile)
62		return json.Unmarshal(data, d.RenameFile)
63	case "delete":
64		d.DeleteFile = new(DeleteFile)
65		return json.Unmarshal(data, d.DeleteFile)
66	}
67	return fmt.Errorf("DocumentChanges: unexpected kind: %q", kind)
68}
69
70func (d *DocumentChange) MarshalJSON() ([]byte, error) {
71	if d.TextDocumentEdit != nil {
72		return json.Marshal(d.TextDocumentEdit)
73	} else if d.CreateFile != nil {
74		return json.Marshal(d.CreateFile)
75	} else if d.RenameFile != nil {
76		return json.Marshal(d.RenameFile)
77	} else if d.DeleteFile != nil {
78		return json.Marshal(d.DeleteFile)
79	}
80	return nil, fmt.Errorf("empty DocumentChanges union value")
81}