Path: blob/main/vendor/github.com/pelletier/go-toml/v2/errors.go
2880 views
package toml12import (3"fmt"4"strconv"5"strings"67"github.com/pelletier/go-toml/v2/internal/danger"8"github.com/pelletier/go-toml/v2/unstable"9)1011// DecodeError represents an error encountered during the parsing or decoding12// of a TOML document.13//14// In addition to the error message, it contains the position in the document15// where it happened, as well as a human-readable representation that shows16// where the error occurred in the document.17type DecodeError struct {18message string19line int20column int21key Key2223human string24}2526// StrictMissingError occurs in a TOML document that does not have a27// corresponding field in the target value. It contains all the missing fields28// in Errors.29//30// Emitted by Decoder when DisallowUnknownFields() was called.31type StrictMissingError struct {32// One error per field that could not be found.33Errors []DecodeError34}3536// Error returns the canonical string for this error.37func (s *StrictMissingError) Error() string {38return "strict mode: fields in the document are missing in the target struct"39}4041// String returns a human readable description of all errors.42func (s *StrictMissingError) String() string {43var buf strings.Builder4445for i, e := range s.Errors {46if i > 0 {47buf.WriteString("\n---\n")48}4950buf.WriteString(e.String())51}5253return buf.String()54}5556type Key []string5758// Error returns the error message contained in the DecodeError.59func (e *DecodeError) Error() string {60return "toml: " + e.message61}6263// String returns the human-readable contextualized error. This string is multi-line.64func (e *DecodeError) String() string {65return e.human66}6768// Position returns the (line, column) pair indicating where the error69// occurred in the document. Positions are 1-indexed.70func (e *DecodeError) Position() (row int, column int) {71return e.line, e.column72}7374// Key that was being processed when the error occurred. The key is present only75// if this DecodeError is part of a StrictMissingError.76func (e *DecodeError) Key() Key {77return e.key78}7980// decodeErrorFromHighlight creates a DecodeError referencing a highlighted81// range of bytes from document.82//83// highlight needs to be a sub-slice of document, or this function panics.84//85// The function copies all bytes used in DecodeError, so that document and86// highlight can be freely deallocated.87//88//nolint:funlen89func wrapDecodeError(document []byte, de *unstable.ParserError) *DecodeError {90offset := danger.SubsliceOffset(document, de.Highlight)9192errMessage := de.Error()93errLine, errColumn := positionAtEnd(document[:offset])94before, after := linesOfContext(document, de.Highlight, offset, 3)9596var buf strings.Builder9798maxLine := errLine + len(after) - 199lineColumnWidth := len(strconv.Itoa(maxLine))100101// Write the lines of context strictly before the error.102for i := len(before) - 1; i > 0; i-- {103line := errLine - i104buf.WriteString(formatLineNumber(line, lineColumnWidth))105buf.WriteString("|")106107if len(before[i]) > 0 {108buf.WriteString(" ")109buf.Write(before[i])110}111112buf.WriteRune('\n')113}114115// Write the document line that contains the error.116117buf.WriteString(formatLineNumber(errLine, lineColumnWidth))118buf.WriteString("| ")119120if len(before) > 0 {121buf.Write(before[0])122}123124buf.Write(de.Highlight)125126if len(after) > 0 {127buf.Write(after[0])128}129130buf.WriteRune('\n')131132// Write the line with the error message itself (so it does not have a line133// number).134135buf.WriteString(strings.Repeat(" ", lineColumnWidth))136buf.WriteString("| ")137138if len(before) > 0 {139buf.WriteString(strings.Repeat(" ", len(before[0])))140}141142buf.WriteString(strings.Repeat("~", len(de.Highlight)))143144if len(errMessage) > 0 {145buf.WriteString(" ")146buf.WriteString(errMessage)147}148149// Write the lines of context strictly after the error.150151for i := 1; i < len(after); i++ {152buf.WriteRune('\n')153line := errLine + i154buf.WriteString(formatLineNumber(line, lineColumnWidth))155buf.WriteString("|")156157if len(after[i]) > 0 {158buf.WriteString(" ")159buf.Write(after[i])160}161}162163return &DecodeError{164message: errMessage,165line: errLine,166column: errColumn,167key: de.Key,168human: buf.String(),169}170}171172func formatLineNumber(line int, width int) string {173format := "%" + strconv.Itoa(width) + "d"174175return fmt.Sprintf(format, line)176}177178func linesOfContext(document []byte, highlight []byte, offset int, linesAround int) ([][]byte, [][]byte) {179return beforeLines(document, offset, linesAround), afterLines(document, highlight, offset, linesAround)180}181182func beforeLines(document []byte, offset int, linesAround int) [][]byte {183var beforeLines [][]byte184185// Walk the document backward from the highlight to find previous lines186// of context.187rest := document[:offset]188backward:189for o := len(rest) - 1; o >= 0 && len(beforeLines) <= linesAround && len(rest) > 0; {190switch {191case rest[o] == '\n':192// handle individual lines193beforeLines = append(beforeLines, rest[o+1:])194rest = rest[:o]195o = len(rest) - 1196case o == 0:197// add the first line only if it's non-empty198beforeLines = append(beforeLines, rest)199200break backward201default:202o--203}204}205206return beforeLines207}208209func afterLines(document []byte, highlight []byte, offset int, linesAround int) [][]byte {210var afterLines [][]byte211212// Walk the document forward from the highlight to find the following213// lines of context.214rest := document[offset+len(highlight):]215forward:216for o := 0; o < len(rest) && len(afterLines) <= linesAround; {217switch {218case rest[o] == '\n':219// handle individual lines220afterLines = append(afterLines, rest[:o])221rest = rest[o+1:]222o = 0223224case o == len(rest)-1:225// add last line only if it's non-empty226afterLines = append(afterLines, rest)227228break forward229default:230o++231}232}233234return afterLines235}236237func positionAtEnd(b []byte) (row int, column int) {238row = 1239column = 1240241for _, c := range b {242if c == '\n' {243row++244column = 1245} else {246column++247}248}249250return251}252253254