@@ -42,7 +42,12 @@ type FieldMismatch struct {
4242
4343// String returns a user friendly message explaining the type mismatch.
4444func (f FieldMismatch ) String () string {
45- return fmt .Sprintf (`expected "%s" to be a %s but it's a %s` , f .Field , f .Expected , f .Actual )
45+ return fmt .Sprintf (
46+ `expected "%s" to be %s but it's %s` ,
47+ f .Field ,
48+ typeNameWithArticle (f .Expected ),
49+ typeNameWithArticle (f .Actual ),
50+ )
4651}
4752
4853// CompareOpts can be used to configure how CompareMapToStruct works.
@@ -295,3 +300,31 @@ func parseField(f reflect.StructField) (name string, ignore bool) {
295300
296301 return tag , false
297302}
303+
304+ // typeNameStartsWithVowel returns true if the type name starts with a vowel.
305+ // This doesn't include "u" as a vowel since words like "user" should be "a user"
306+ // and not "an user".
307+ func typeNameStartsWithVowel (t string ) bool {
308+ t = strings .TrimLeft (t , "*" )
309+
310+ switch strings .ToLower (t [:1 ]) {
311+ case "a" , "e" , "i" , "o" :
312+ return true
313+ }
314+
315+ return false
316+ }
317+
318+ // typeNameWithArticle returns the type name with an indefinite article ("a" or "an").
319+ // If the type name is "null" it just returns "null".
320+ func typeNameWithArticle (t string ) string {
321+ if t == "null" {
322+ return t
323+ }
324+
325+ if typeNameStartsWithVowel (t ) {
326+ return "an " + t
327+ } else {
328+ return "a " + t
329+ }
330+ }
0 commit comments