pkg/errors/template.go
Rama main · réplica de lectura del repositorio original.
package errors
import (
goAst "go/ast"
goToken "go/token"
"reflect"
"strings"
)
// Template represents a template for a new error.
//
// It itself is not an error, but can be used to initialize a new [errorinsrc.ErrInSrc].
type Template struct {
Code int
Title string
Summary string
Detail string
Cause error
Locations []SrcLocation
AlwaysIncludeStack bool
}
// TemplateOption can be passed into the [Range] when creating a new Template
type TemplateOption func(*Template)
// AlwaysIncludeStack will setup a Template so it always includes a stack trace
// even in a production build of Encore.
func AlwaysIncludeStack() TemplateOption {
return func(template *Template) {
template.AlwaysIncludeStack = true
}
}
// WithDetails will setup a template so it uses a different details to the
// range default
func WithDetails(details string) TemplateOption {
return func(template *Template) {
template.Detail = details
}
}
// PrependDetails will setup a template so it prepends the given details to the
// range default
func PrependDetails(details string) TemplateOption {
return func(template *Template) {
if template.Detail != "" {
details = details + "\n\n" + template.Detail
}
template.Detail = details
}
}
// MarkAsInternalError will setup a template so it is reported as an internal error.
//
// This means that the error will be reported to the user as an internal error
// with a link to the Encore issue tracker and will include a stack trace.
func MarkAsInternalError() TemplateOption {
return func(template *Template) {
template.AlwaysIncludeStack = true
template.Title = "Internal Error: " + template.Title
template.Detail = "This is a bug in Encore and should not have occurred. Please report this issue to the " +
"Encore team either on Github at https://github.com/encoredev/encore/issues/new and include this error."
}
}
// WithDetails will replace the details of the template with the given details
func (t Template) WithDetails(details string) Template {
t.Detail = details
return t
}
// Wrapping wraps the given error with the template.
//
// It will append the given error to the summary of the template
// as well as setting the cause of the template to the given error.
func (t Template) Wrapping(err error) Template {
if err == nil {
return t
}
t.Summary += "\n\n" + err.Error()
t.Summary = strings.TrimSpace(t.Summary)
t.Cause = err
return t
}
// atLocation is a helper method for the various with methods
func (t Template) atLocation(location SrcLocation, options []LocationOption) Template {
for _, o := range options {
o(&location)
}
t.Locations = append([]SrcLocation{location}, t.Locations...)
return t
}
// InFile adds the given file as a src of the error location
//
// Note: It is preferable to use one of the other location functions
// as they will render the source around the error, not just the file name
func (t Template) InFile(filepath string, options ...LocationOption) Template {
if filepath == "" {
return t
}
return t.atLocation(SrcLocation{Kind: LocFile, Filepath: filepath}, options)
}
// AtGoNode adds the given Go node to the template. If the node is nil, nothing happens.
//
// You can use the [LocationOption]s to add additional information to the location.
//
// Example:
//
// errMyErrorTemplate.AtGoNode(node, errtmp.AsHelp("this is where it was defined before"))
func (t Template) AtGoNode(node goAst.Node, options ...LocationOption) Template {
if node == nil {
return t
}
// In some cases the node may be a "typed nil" which isn't caught by the check above.
// Handle that separately, as otherwise we get unexpected panics later when
// trying to call .Pos().
if v := reflect.ValueOf(node); v.Kind() == reflect.Pointer && v.IsNil() {
return t
}
return t.atLocation(SrcLocation{Kind: LocGoNode, GoNode: node}, options)
}
// AtGoPos adds the given start and end [token.Pos] to the template. If both positions are token.NoPos, nothing happens.
// If one of the two positions are token.NoPos, the other position will be used.
//
// It is valid to use the same value for start and end positions, in which case the error will estimate which Node
// you are referencing.
//
// Example:
//
// errMyErrorTemplate.AtGoPos(start, token.NoPos, errtmp.AsHelp("this is where it was defined before"))
func (t Template) AtGoPos(start, end goToken.Pos, options ...LocationOption) Template {
switch {
case start == goToken.NoPos && end == goToken.NoPos:
return t
case start == goToken.NoPos && end != goToken.NoPos:
start = end
case end == goToken.NoPos && start != goToken.NoPos:
end = start
}
return t.atLocation(SrcLocation{Kind: LocGoPos, GoStartPos: start, GoEndPos: end}, options)
}
// AtGoPosition adds the given Go positions to the template.
//
// It is valid to use the same value for start and end positions, in which case the error will estimate which Node
// you are referencing.
//
// Example:
//
// errMyErrorTemplate.AtGoPosition(start, end, errtmp.AsHelp("this is where it was defined before"))
func (t Template) AtGoPosition(start, end goToken.Position, options ...LocationOption) Template {
return t.atLocation(SrcLocation{Kind: LocGoPositions, GoStartPosition: start, GoEndPosition: end}, options)
}