1package cmp
2
3import (
4	"bytes"
5	"fmt"
6	"go/ast"
7	"text/template"
8
9	"gotest.tools/v3/internal/source"
10)
11
12// A Result of a Comparison.
13type Result interface {
14	Success() bool
15}
16
17// StringResult is an implementation of Result that reports the error message
18// string verbatim and does not provide any templating or formatting of the
19// message.
20type StringResult struct {
21	success bool
22	message string
23}
24
25// Success returns true if the comparison was successful.
26func (r StringResult) Success() bool {
27	return r.success
28}
29
30// FailureMessage returns the message used to provide additional information
31// about the failure.
32func (r StringResult) FailureMessage() string {
33	return r.message
34}
35
36// ResultSuccess is a constant which is returned by a ComparisonWithResult to
37// indicate success.
38var ResultSuccess = StringResult{success: true}
39
40// ResultFailure returns a failed Result with a failure message.
41func ResultFailure(message string) StringResult {
42	return StringResult{message: message}
43}
44
45// ResultFromError returns ResultSuccess if err is nil. Otherwise ResultFailure
46// is returned with the error message as the failure message.
47func ResultFromError(err error) Result {
48	if err == nil {
49		return ResultSuccess
50	}
51	return ResultFailure(err.Error())
52}
53
54type templatedResult struct {
55	success  bool
56	template string
57	data     map[string]interface{}
58}
59
60func (r templatedResult) Success() bool {
61	return r.success
62}
63
64func (r templatedResult) FailureMessage(args []ast.Expr) string {
65	msg, err := renderMessage(r, args)
66	if err != nil {
67		return fmt.Sprintf("failed to render failure message: %s", err)
68	}
69	return msg
70}
71
72// ResultFailureTemplate returns a Result with a template string and data which
73// can be used to format a failure message. The template may access data from .Data,
74// the comparison args with the callArg function, and the formatNode function may
75// be used to format the call args.
76func ResultFailureTemplate(template string, data map[string]interface{}) Result {
77	return templatedResult{template: template, data: data}
78}
79
80func renderMessage(result templatedResult, args []ast.Expr) (string, error) {
81	tmpl := template.New("failure").Funcs(template.FuncMap{
82		"formatNode": source.FormatNode,
83		"callArg": func(index int) ast.Expr {
84			if index >= len(args) {
85				return nil
86			}
87			return args[index]
88		},
89	})
90	var err error
91	tmpl, err = tmpl.Parse(result.template)
92	if err != nil {
93		return "", err
94	}
95	buf := new(bytes.Buffer)
96	err = tmpl.Execute(buf, map[string]interface{}{
97		"Data": result.data,
98	})
99	return buf.String(), err
100}
101