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