github.com/graemephi/kahugo@v0.62.3-0.20211121071557-d78c0423784d/common/herrors/file_error.go (about) 1 // Copyright 2018 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package herrors 15 16 import ( 17 "encoding/json" 18 19 "github.com/gohugoio/hugo/common/text" 20 21 "github.com/pkg/errors" 22 ) 23 24 var _ causer = (*fileError)(nil) 25 26 // FileError represents an error when handling a file: Parsing a config file, 27 // execute a template etc. 28 type FileError interface { 29 error 30 31 text.Positioner 32 33 // A string identifying the type of file, e.g. JSON, TOML, markdown etc. 34 Type() string 35 } 36 37 var _ FileError = (*fileError)(nil) 38 39 type fileError struct { 40 position text.Position 41 42 fileType string 43 44 cause error 45 } 46 47 // Position returns the text position of this error. 48 func (e fileError) Position() text.Position { 49 return e.position 50 } 51 52 func (e *fileError) Type() string { 53 return e.fileType 54 } 55 56 func (e *fileError) Error() string { 57 if e.cause == nil { 58 return "" 59 } 60 return e.cause.Error() 61 } 62 63 func (f *fileError) Cause() error { 64 return f.cause 65 } 66 67 // NewFileError creates a new FileError. 68 func NewFileError(fileType string, offset, lineNumber, columnNumber int, err error) FileError { 69 pos := text.Position{Offset: offset, LineNumber: lineNumber, ColumnNumber: columnNumber} 70 return &fileError{cause: err, fileType: fileType, position: pos} 71 } 72 73 // UnwrapFileError tries to unwrap a FileError from err. 74 // It returns nil if this is not possible. 75 func UnwrapFileError(err error) FileError { 76 for err != nil { 77 switch v := err.(type) { 78 case FileError: 79 return v 80 case causer: 81 err = v.Cause() 82 default: 83 return nil 84 } 85 } 86 return nil 87 } 88 89 // ToFileErrorWithOffset will return a new FileError with a line number 90 // with the given offset from the original. 91 func ToFileErrorWithOffset(fe FileError, offset int) FileError { 92 pos := fe.Position() 93 return ToFileErrorWithLineNumber(fe, pos.LineNumber+offset) 94 } 95 96 // ToFileErrorWithOffset will return a new FileError with the given line number. 97 func ToFileErrorWithLineNumber(fe FileError, lineNumber int) FileError { 98 pos := fe.Position() 99 pos.LineNumber = lineNumber 100 return &fileError{cause: fe, fileType: fe.Type(), position: pos} 101 } 102 103 // ToFileError will convert the given error to an error supporting 104 // the FileError interface. 105 func ToFileError(fileType string, err error) FileError { 106 for _, handle := range lineNumberExtractors { 107 lno, col := handle(err) 108 offset, typ := extractOffsetAndType(err) 109 if fileType == "" { 110 fileType = typ 111 } 112 113 if lno > 0 || offset != -1 { 114 return NewFileError(fileType, offset, lno, col, err) 115 } 116 } 117 // Fall back to the pointing to line number 1. 118 return NewFileError(fileType, -1, 1, 1, err) 119 } 120 121 func extractOffsetAndType(e error) (int, string) { 122 e = errors.Cause(e) 123 switch v := e.(type) { 124 case *json.UnmarshalTypeError: 125 return int(v.Offset), "json" 126 case *json.SyntaxError: 127 return int(v.Offset), "json" 128 default: 129 return -1, "" 130 } 131 }