Ctrl K
ComunidadGuías…

pkg/errors/template.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
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)
}