a646ffd9fa
Thread byte offset information through all error creation sites, eliminating the need for SubsliceOffset to recover position from pointer comparison. Changes: - Add Offset field to ParserError struct - Add offset parameter to NewParserError - Add Parser.offsetOf helper for suffix-length arithmetic - Thread base offset through scanner functions (scanComment, scanBasicString, scanMultilineBasicString, scanLiteralString, scanMultilineLiteralString, scanWindowsNewline) - Thread base offset through standalone functions (expect, hexToRune) - Thread base offset through all decode functions (parseInteger, parseFloat, parseLocalDate, parseLocalTime, parseLocalDateTime, parseDateTime, checkAndRemoveUnderscores*) - Update all unmarshaler call sites to pass value.Raw.Offset - Update localtime.go UnmarshalText methods with base=0 - Update strict.go to populate Offset from key ranges - Change wrapDecodeError to read de.Offset directly - Change Utf8TomlValidAlreadyEscaped to return int index (-1 if valid) instead of a byte subslice - Unexport SubsliceOffset (now only used internally by Range()) This makes error positions self-describing: each ParserError carries its own byte offset, so callers no longer need the original document slice and address arithmetic to determine where an error occurred. Co-authored-by: Thomas Pelletier <thomas@pelletier.dev>
117 lines
2.0 KiB
Go
117 lines
2.0 KiB
Go
package toml
|
|
|
|
import (
|
|
"github.com/pelletier/go-toml/v2/internal/tracker"
|
|
"github.com/pelletier/go-toml/v2/unstable"
|
|
)
|
|
|
|
type strict struct {
|
|
Enabled bool
|
|
|
|
// Tracks the current key being processed.
|
|
key tracker.KeyTracker
|
|
|
|
missing []unstable.ParserError
|
|
|
|
// Reference to the document for computing key ranges.
|
|
doc []byte
|
|
}
|
|
|
|
func (s *strict) EnterTable(node *unstable.Node) {
|
|
if !s.Enabled {
|
|
return
|
|
}
|
|
|
|
s.key.UpdateTable(node)
|
|
}
|
|
|
|
func (s *strict) EnterArrayTable(node *unstable.Node) {
|
|
if !s.Enabled {
|
|
return
|
|
}
|
|
|
|
s.key.UpdateArrayTable(node)
|
|
}
|
|
|
|
func (s *strict) EnterKeyValue(node *unstable.Node) {
|
|
if !s.Enabled {
|
|
return
|
|
}
|
|
|
|
s.key.Push(node)
|
|
}
|
|
|
|
func (s *strict) ExitKeyValue(node *unstable.Node) {
|
|
if !s.Enabled {
|
|
return
|
|
}
|
|
|
|
s.key.Pop(node)
|
|
}
|
|
|
|
func (s *strict) MissingTable(node *unstable.Node) {
|
|
if !s.Enabled {
|
|
return
|
|
}
|
|
|
|
loc, offset := s.keyLocation(node)
|
|
s.missing = append(s.missing, unstable.ParserError{
|
|
Highlight: loc,
|
|
Message: "missing table",
|
|
Key: s.key.Key(),
|
|
Offset: offset,
|
|
})
|
|
}
|
|
|
|
func (s *strict) MissingField(node *unstable.Node) {
|
|
if !s.Enabled {
|
|
return
|
|
}
|
|
|
|
loc, offset := s.keyLocation(node)
|
|
s.missing = append(s.missing, unstable.ParserError{
|
|
Highlight: loc,
|
|
Message: "missing field",
|
|
Key: s.key.Key(),
|
|
Offset: offset,
|
|
})
|
|
}
|
|
|
|
func (s *strict) Error(doc []byte) error {
|
|
if !s.Enabled || len(s.missing) == 0 {
|
|
return nil
|
|
}
|
|
|
|
err := &StrictMissingError{
|
|
Errors: make([]DecodeError, 0, len(s.missing)),
|
|
}
|
|
|
|
for _, derr := range s.missing {
|
|
derr := derr
|
|
err.Errors = append(err.Errors, *wrapDecodeError(doc, &derr))
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (s *strict) keyLocation(node *unstable.Node) ([]byte, int) {
|
|
k := node.Key()
|
|
|
|
hasOne := k.Next()
|
|
if !hasOne {
|
|
panic("should not be called with empty key")
|
|
}
|
|
|
|
firstRaw := k.Node().Raw
|
|
lastRaw := firstRaw
|
|
|
|
for k.Next() {
|
|
lastRaw = k.Node().Raw
|
|
}
|
|
|
|
start := firstRaw.Offset
|
|
end := lastRaw.Offset + lastRaw.Length
|
|
|
|
return s.doc[start:end], int(start)
|
|
}
|