1package toml
2
3import (
4	"github.com/pelletier/go-toml/v2/internal/ast"
5	"github.com/pelletier/go-toml/v2/internal/danger"
6	"github.com/pelletier/go-toml/v2/internal/tracker"
7)
8
9type strict struct {
10	Enabled bool
11
12	// Tracks the current key being processed.
13	key tracker.KeyTracker
14
15	missing []decodeError
16}
17
18func (s *strict) EnterTable(node *ast.Node) {
19	if !s.Enabled {
20		return
21	}
22
23	s.key.UpdateTable(node)
24}
25
26func (s *strict) EnterArrayTable(node *ast.Node) {
27	if !s.Enabled {
28		return
29	}
30
31	s.key.UpdateArrayTable(node)
32}
33
34func (s *strict) EnterKeyValue(node *ast.Node) {
35	if !s.Enabled {
36		return
37	}
38
39	s.key.Push(node)
40}
41
42func (s *strict) ExitKeyValue(node *ast.Node) {
43	if !s.Enabled {
44		return
45	}
46
47	s.key.Pop(node)
48}
49
50func (s *strict) MissingTable(node *ast.Node) {
51	if !s.Enabled {
52		return
53	}
54
55	s.missing = append(s.missing, decodeError{
56		highlight: keyLocation(node),
57		message:   "missing table",
58		key:       s.key.Key(),
59	})
60}
61
62func (s *strict) MissingField(node *ast.Node) {
63	if !s.Enabled {
64		return
65	}
66
67	s.missing = append(s.missing, decodeError{
68		highlight: keyLocation(node),
69		message:   "missing field",
70		key:       s.key.Key(),
71	})
72}
73
74func (s *strict) Error(doc []byte) error {
75	if !s.Enabled || len(s.missing) == 0 {
76		return nil
77	}
78
79	err := &StrictMissingError{
80		Errors: make([]DecodeError, 0, len(s.missing)),
81	}
82
83	for _, derr := range s.missing {
84		derr := derr
85		err.Errors = append(err.Errors, *wrapDecodeError(doc, &derr))
86	}
87
88	return err
89}
90
91func keyLocation(node *ast.Node) []byte {
92	k := node.Key()
93
94	hasOne := k.Next()
95	if !hasOne {
96		panic("should not be called with empty key")
97	}
98
99	start := k.Node().Data
100	end := k.Node().Data
101
102	for k.Next() {
103		end = k.Node().Data
104	}
105
106	return danger.BytesRange(start, end)
107}
108