forked from toolshed/abra
		
	
		
			
				
	
	
		
			111 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			111 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package cmp
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"fmt"
 | 
						|
	"go/ast"
 | 
						|
	"reflect"
 | 
						|
	"text/template"
 | 
						|
 | 
						|
	"gotest.tools/v3/internal/source"
 | 
						|
)
 | 
						|
 | 
						|
// A Result of a [Comparison].
 | 
						|
type Result interface {
 | 
						|
	Success() bool
 | 
						|
}
 | 
						|
 | 
						|
// StringResult is an implementation of [Result] that reports the error message
 | 
						|
// string verbatim and does not provide any templating or formatting of the
 | 
						|
// message.
 | 
						|
type StringResult struct {
 | 
						|
	success bool
 | 
						|
	message string
 | 
						|
}
 | 
						|
 | 
						|
// Success returns true if the comparison was successful.
 | 
						|
func (r StringResult) Success() bool {
 | 
						|
	return r.success
 | 
						|
}
 | 
						|
 | 
						|
// FailureMessage returns the message used to provide additional information
 | 
						|
// about the failure.
 | 
						|
func (r StringResult) FailureMessage() string {
 | 
						|
	return r.message
 | 
						|
}
 | 
						|
 | 
						|
// ResultSuccess is a constant which is returned by a [Comparison] to
 | 
						|
// indicate success.
 | 
						|
var ResultSuccess = StringResult{success: true}
 | 
						|
 | 
						|
// ResultFailure returns a failed [Result] with a failure message.
 | 
						|
func ResultFailure(message string) StringResult {
 | 
						|
	return StringResult{message: message}
 | 
						|
}
 | 
						|
 | 
						|
// ResultFromError returns [ResultSuccess] if err is nil. Otherwise [ResultFailure]
 | 
						|
// is returned with the error message as the failure message.
 | 
						|
func ResultFromError(err error) Result {
 | 
						|
	if err == nil {
 | 
						|
		return ResultSuccess
 | 
						|
	}
 | 
						|
	return ResultFailure(err.Error())
 | 
						|
}
 | 
						|
 | 
						|
type templatedResult struct {
 | 
						|
	template string
 | 
						|
	data     map[string]interface{}
 | 
						|
}
 | 
						|
 | 
						|
func (r templatedResult) Success() bool {
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
func (r templatedResult) FailureMessage(args []ast.Expr) string {
 | 
						|
	msg, err := renderMessage(r, args)
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Sprintf("failed to render failure message: %s", err)
 | 
						|
	}
 | 
						|
	return msg
 | 
						|
}
 | 
						|
 | 
						|
func (r templatedResult) UpdatedExpected(stackIndex int) error {
 | 
						|
	// TODO: would be nice to have structured data instead of a map
 | 
						|
	return source.UpdateExpectedValue(stackIndex+1, r.data["x"], r.data["y"])
 | 
						|
}
 | 
						|
 | 
						|
// ResultFailureTemplate returns a [Result] with a template string and data which
 | 
						|
// can be used to format a failure message. The template may access data from .Data,
 | 
						|
// the comparison args with the callArg function, and the formatNode function may
 | 
						|
// be used to format the call args.
 | 
						|
func ResultFailureTemplate(template string, data map[string]interface{}) Result {
 | 
						|
	return templatedResult{template: template, data: data}
 | 
						|
}
 | 
						|
 | 
						|
func renderMessage(result templatedResult, args []ast.Expr) (string, error) {
 | 
						|
	tmpl := template.New("failure").Funcs(template.FuncMap{
 | 
						|
		"formatNode": source.FormatNode,
 | 
						|
		"callArg": func(index int) ast.Expr {
 | 
						|
			if index >= len(args) {
 | 
						|
				return nil
 | 
						|
			}
 | 
						|
			return args[index]
 | 
						|
		},
 | 
						|
		// TODO: any way to include this from ErrorIS instead of here?
 | 
						|
		"notStdlibErrorType": func(typ interface{}) bool {
 | 
						|
			r := reflect.TypeOf(typ)
 | 
						|
			return r != stdlibFmtErrorType && r != stdlibErrorNewType
 | 
						|
		},
 | 
						|
	})
 | 
						|
	var err error
 | 
						|
	tmpl, err = tmpl.Parse(result.template)
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
	buf := new(bytes.Buffer)
 | 
						|
	err = tmpl.Execute(buf, map[string]interface{}{
 | 
						|
		"Data": result.data,
 | 
						|
	})
 | 
						|
	return buf.String(), err
 | 
						|
}
 |