summaryrefslogtreecommitdiffstats
path: root/utils/jsonutils/json.go
blob: da77a2b6ba333aa658323a2a8f5f76a8ba856c17 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.

package jsonutils

import (
	"bytes"
	"encoding/json"

	"github.com/pkg/errors"
)

type HumanizedJsonError struct {
	Err       error
	Line      int
	Character int
}

func (e *HumanizedJsonError) Error() string {
	return e.Err.Error()
}

// HumanizeJsonError extracts error offsets and annotates the error with useful context
func HumanizeJsonError(err error, data []byte) error {
	if syntaxError, ok := err.(*json.SyntaxError); ok {
		return NewHumanizedJsonError(syntaxError, data, syntaxError.Offset)
	} else if unmarshalError, ok := err.(*json.UnmarshalTypeError); ok {
		return NewHumanizedJsonError(unmarshalError, data, unmarshalError.Offset)
	} else {
		return err
	}
}

func NewHumanizedJsonError(err error, data []byte, offset int64) *HumanizedJsonError {
	if err == nil {
		return nil
	}

	if offset < 0 || offset > int64(len(data)) {
		return &HumanizedJsonError{
			Err: errors.Wrapf(err, "invalid offset %d", offset),
		}
	}

	lineSep := []byte{'\n'}

	line := bytes.Count(data[:offset], lineSep) + 1
	lastLineOffset := bytes.LastIndex(data[:offset], lineSep)
	character := int(offset) - (lastLineOffset + 1) + 1

	return &HumanizedJsonError{
		Line:      line,
		Character: character,
		Err:       errors.Wrapf(err, "parsing error at line %d, character %d", line, character),
	}
}