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  }