1 package tri
2 3 import (
4 "time"
5 "reflect"
6 "errors"
7 "fmt"
8 "unicode"
9 )
10 11 // Validate checks to ensure the contents of this node type satisfy constraints.
12 // Brief only contains one thing, so we make sure it has it - one string. This string may not contain any type of control characters, and is limited to 80 characters in length.
13 func (r *Brief) Validate() error {
14 15 R := (*r)
16 if len(R) != 1 {
17 return errors.New("Brief field must have (only) one item")
18 }
19 s, ok := R[0].(string)
20 if !ok {
21 return errors.New("Brief's mandatory field is not a string")
22 }
23 if len(s) > 80 {
24 return errors.New("Brief's text may not be over 80 characters in length")
25 }
26 for i, x := range s {
27 if unicode.IsControl(x) {
28 return fmt.Errorf("Brief text may not contain any control characters, one found at position %d", i)
29 }
30 }
31 return nil
32 }
33 34 // Validate checks to ensure the contents of this node type satisfy constraints.
35 // This validator only has to check the elements of the slice are zero or more Command items, and a valid name at index 0.
36 func (r *Command) Validate() error {
37 38 R := *r
39 if len(R) < 1 {
40 return errors.New("empty Command")
41 }
42 s, ok := R[0].(string)
43 if !ok {
44 return fmt.Errorf("first element of Command must be a string")
45 }
46 if e := ValidName(s); e != nil {
47 return fmt.Errorf("error in name of Command: %v", e)
48 }
49 // validSet is an array of 4 elements that represent the presence of the 4 mandatory parts.
50 var validSet [2]bool
51 brief, handler := 0, 1
52 var singleSet [4]bool
53 usage, short, help, examples := 0, 1, 2, 3
54 for i, x := range R[1:] {
55 switch c := x.(type) {
56 case Short:
57 if singleSet[short] {
58 return fmt.Errorf("only one Short field allowed in Command")
59 }
60 singleSet[short] = true
61 62 e := c.Validate()
63 if e != nil {
64 return fmt.Errorf("error in Command at index %d: %v", i, e)
65 }
66 case Brief:
67 if validSet[brief] {
68 return fmt.Errorf("only one Brief permitted in a Command, second found at index %d", i)
69 }
70 validSet[brief] = true
71 e := c.Validate()
72 if e != nil {
73 return fmt.Errorf("error in Command at index %d: %v", i, e)
74 }
75 case Usage:
76 if singleSet[usage] {
77 return fmt.Errorf("only one Usage field allowed in Command")
78 }
79 singleSet[usage] = true
80 e := c.Validate()
81 if e != nil {
82 return fmt.Errorf("error in Command at index %d: %v", i, e)
83 }
84 case Help:
85 if singleSet[help] {
86 return fmt.Errorf("only one Help field allowed in Command")
87 }
88 singleSet[help] = true
89 90 e := c.Validate()
91 if e != nil {
92 return fmt.Errorf("error in Command at index %d: %v", i, e)
93 }
94 case Examples:
95 if singleSet[examples] {
96 return fmt.Errorf("only one Examples field allowed in Command")
97 }
98 singleSet[examples] = true
99 100 e := c.Validate()
101 if e != nil {
102 return fmt.Errorf("error in Command at index %d: %v", i, e)
103 }
104 case Var:
105 e := c.Validate()
106 if e != nil {
107 return fmt.Errorf("error in Command at index %d: %v", i, e)
108 }
109 case Trigger:
110 e := c.Validate()
111 if e != nil {
112 return fmt.Errorf("error in Command at index %d: %v", i, e)
113 }
114 case func(*Tri) int:
115 if validSet[handler] {
116 return fmt.Errorf("only one Handler permitted in a Command, second found at index %d", i)
117 }
118 validSet[handler] = true
119 if c == nil {
120 return fmt.Errorf("nil handler in Command found at index %d", i)
121 }
122 default:
123 return fmt.Errorf("invalid type present in Command: %v", reflect.TypeOf(c))
124 }
125 }
126 if !validSet[brief] {
127 return errors.New("Brief field must be present")
128 }
129 if !validSet[handler] {
130 return errors.New("Command must have a handler")
131 }
132 return nil
133 }
134 135 // Validate checks to ensure the contents of this node type satisfy constraints.
136 // This validator only triggers the validator on its elements.
137 func (r *Commands) Validate() error {
138 139 R := (*r)
140 for i, x := range R {
141 e := x.Validate()
142 if e != nil {
143 return fmt.Errorf("error in element %d of Commands list: %v", i, e)
144 }
145 }
146 return nil
147 }
148 149 // Validate checks to ensure the contents of this node type satisfy constraints.
150 // The only constraint on the Default subtype is that it contains at only one element, the value is checked for correct typing by the Commands validator.
151 func (r *Default) Validate() error {
152 153 R := (*r)
154 if len(R) != 1 {
155 return errors.New("the Default container must only contain one element")
156 }
157 return nil
158 }
159 160 // Validate checks to ensure the contents of this node type satisfy constraints.
161 // The constraint of DefaultCommand is that it has at least one element, and that the 0 element is a string. The check for the command name's presence in the Commands set is in the Tri validator.
162 func (r *DefaultCommand) Validate() error {
163 164 R := (*r)
165 if len(R) != 1 {
166 return errors.New(
167 "the DefaultCommand element must contain only one element")
168 }
169 s, ok := R[0].(string)
170 if !ok {
171 return errors.New("element 0 of DefaultCommand must be a string")
172 }
173 if e := ValidName(s); e != nil {
174 return fmt.Errorf("error in DefaultCommand: %v", e)
175 }
176 return nil
177 }
178 179 // Validate checks to ensure the contents of this node type satisfy constraints.
180 // RunAfter is a simple flag that indicates by existence of an empty value, so it is an error if it has anything inside it.
181 func (r *DefaultOn) Validate() error {
182 183 R := *r
184 if len(R) > 0 {
185 return errors.New(
186 "DefaultOn may not contain anything, empty declaration only")
187 }
188 return nil
189 }
190 191 // Validate checks to ensure the contents of this node type satisfy constraints.
192 // The constraints of examples are minimum two elements and all elements are strings. The intent is the even numbered items are snippets showing invocation and a description string of the same format as Brief{}.
193 func (r *Examples) Validate() error {
194 195 R := *r
196 if len(R) < 2 {
197 return errors.New("Examples field may not be empty")
198 }
199 if len(R)%2 != 0 {
200 return fmt.Errorf(
201 "Examples must be in pairs, odd number of elements found")
202 }
203 for i, x := range R {
204 _, ok := x.(string)
205 if !ok {
206 return fmt.Errorf(
207 "Examples elements may only be strings, element %d is not a string", i)
208 }
209 }
210 211 for i := 1; i <= len(R)-1; i += 2 {
212 if len(R[i-1].(string)) > 40 {
213 return errors.New(
214 "Examples example text may not be over 4 characters in length")
215 }
216 if len(R[i].(string)) > 80 {
217 return errors.New(
218 "Examples explainer text may not be over 80 characters in length")
219 }
220 for i, x := range R[i].(string) {
221 if unicode.IsControl(x) {
222 return fmt.Errorf(
223 "Examples even numbered field string may not contain control characters, one found at index %d", i)
224 }
225 }
226 }
227 return nil
228 }
229 230 // Validate checks to ensure the contents of this node type satisfy constraints.
231 // A group must contain one string, anything else is invalid. It also has the same limitation as a name - only letters.
232 func (r *Group) Validate() error {
233 234 R := *r
235 if len(R) != 1 {
236 return errors.New("Group must (only) contain one element")
237 }
238 s, ok := R[0].(string)
239 if !ok {
240 return errors.New("Group element must be a string")
241 }
242 if e := ValidName(s); e != nil {
243 return fmt.Errorf("error in name of Group: %v", e)
244 }
245 return nil
246 }
247 248 // Validate checks to ensure the contents of this node type satisfy constraints.
249 // Help may only contain one string. It will be parsed as markdown format and possibly can be set to style it with ANSI codes.
250 func (r *Help) Validate() error {
251 252 R := *r
253 if len(R) != 1 {
254 return errors.New("Help field must contain (only) one item")
255 }
256 _, ok := R[0].(string)
257 if !ok {
258 return errors.New("Help field element is not a string")
259 }
260 return nil
261 }
262 263 // Validate checks to ensure the contents of this node type satisfy constraints.
264 // RunAfter is a simple flag that indicates by existence of an empty value, so it is an error if it has anything inside it.
265 func (r *RunAfter) Validate() error {
266 267 R := *r
268 if len(R) > 0 {
269 return errors.New(
270 "RunAfter may not contain anything, empty declaration only")
271 }
272 return nil
273 }
274 275 // Validate checks to ensure the contents of this node type satisfy constraints.
276 // Short names contain only a single Rune variable.
277 func (r *Short) Validate() error {
278 279 R := *r
280 if len(R) != 1 {
281 return errors.New("Short name item must contain (only) one item")
282 }
283 s, ok := R[0].(rune)
284 if !ok {
285 return errors.New("Short's element must be a rune (enclose in '')")
286 }
287 if !(unicode.IsDigit(s) || unicode.IsLetter(s)) {
288 return errors.New("Short element is not a letter or number")
289 }
290 return nil
291 }
292 293 // Validate checks to ensure the contents of this node type satisfy constraints.
294 // Slot may only contain one type of element. The type check is in the Var, here we only ensure the slots contain pointers to the same type, the parser will put the final parsed value in all of them. Multiple variables are permitted here to enable the configuration of more than one application.
295 func (r *Slot) Validate() error {
296 297 R := *r
298 var slotTypes []reflect.Type
299 for _, x := range R {
300 slotTypes = append(slotTypes, reflect.TypeOf(x))
301 }
302 for i, x := range slotTypes {
303 if i > 0 {
304 if slotTypes[i] != slotTypes[i-1] {
305 return fmt.Errorf("slot contains more than one type of variable, found %v at index %d", x, i)
306 }
307 }
308 }
309 for _, x := range R {
310 if reflect.ValueOf(x).Kind() != reflect.Ptr {
311 return fmt.Errorf("slot contains non-pointer type")
312 }
313 }
314 315 return nil
316 }
317 318 // Validate checks to ensure the contents of this node type satisfy constraints.
319 // Terminates is a flag value, and may not contain anything.
320 func (r *Terminates) Validate() error {
321 322 R := *r
323 if len(R) > 0 {
324 return errors.New("Terminates type may not contain anything")
325 }
326 return nil
327 }
328 329 // Validate checks to ensure the contents of this node type satisfy constraints.
330 // A Tri, the base type, in a declaration must contain a name as first element, a Brief, Version and a Commands item, and only one of each. Also, this and several other subtypes of Tri.
331 func (r *Tri) Validate() error {
332 R := *r
333 if len(R) < 3 {
334 return errors.New("a Tri must contain at least 3 elements: name, Brief and Version")
335 }
336 // validSet is an array of 4 elements that represent the presence of the 4 mandatory parts.
337 var validSet [2]bool
338 brief, version := 0, 1
339 var singleSet [3]bool
340 defcom, commands := 0, 1
341 n, ok := R[0].(string)
342 if !ok {
343 return errors.New("first element of a Tri must be a string")
344 }
345 if e := ValidName(n); e != nil {
346 return fmt.Errorf("error in name of Tri: %v", e)
347 }
348 349 // The mandatory elements also may not be repeated:
350 for i, x := range R {
351 if i == 0 {
352 continue
353 }
354 switch y := x.(type) {
355 case Brief:
356 if validSet[brief] {
357 return fmt.Errorf(
358 "Tri contains more than one Brief, second found at index %d", i)
359 }
360 validSet[brief] = true
361 if e := y.Validate(); e != nil {
362 return fmt.Errorf("Tri field %d: %s", i, e)
363 }
364 case Version:
365 if validSet[version] {
366 return fmt.Errorf(
367 "Tri contains more than one Version, second found at index %d", i)
368 }
369 validSet[version] = true
370 if e := y.Validate(); e != nil {
371 return fmt.Errorf("Tri field %d: %s", i, e)
372 }
373 validSet[version] = true
374 case Commands:
375 if singleSet[commands] {
376 return fmt.Errorf(
377 "Tri contains more than one Commands, second found at index %d", i)
378 }
379 singleSet[commands] = true
380 e := y.Validate()
381 if e != nil {
382 return fmt.Errorf("error in Tri field %d: %s", i, e)
383 }
384 case Var:
385 e := y.Validate()
386 if e != nil {
387 return fmt.Errorf("error in Tri at index %d: %v", i, e)
388 }
389 case Trigger:
390 e := y.Validate()
391 if e != nil {
392 return fmt.Errorf("error in Tri at index %d: %v", i, e)
393 }
394 case DefaultCommand:
395 if singleSet[defcom] {
396 return fmt.Errorf(
397 "Tri contains more than one DefaultCommand, second found at index %d", i)
398 }
399 singleSet[defcom] = true
400 e := y.Validate()
401 if e != nil {
402 return fmt.Errorf("Tri field %d: %s", i, e)
403 }
404 commname := y[0].(string)
405 // DefaultCommand must match in its name one of the Command items in also present Commands array
406 foundComm := false
407 foundDefComm := false
408 for _, a := range R {
409 switch c := a.(type) {
410 case Commands:
411 foundComm = true
412 for _, b := range c {
413 if b[0].(string) == commname {
414 foundDefComm = true
415 }
416 }
417 default:
418 }
419 }
420 if !foundComm {
421 return errors.New("DefaultCommand with no Commands array present")
422 } else if !foundDefComm {
423 return errors.New("DefaultCommand found with no matching Command")
424 }
425 426 default:
427 return fmt.Errorf(
428 "Tri contains an element type it may not contain at index %d", i)
429 }
430 }
431 switch {
432 case !validSet[brief]:
433 return errors.New("Tri is missing its Brief field")
434 case !validSet[version]:
435 return errors.New("Tri is missing its Version field")
436 }
437 return nil
438 }
439 440 // Validate checks to ensure the contents of this node type satisfy constraints.
441 // Trigger must contain (one) name, Brief and Handler, and nothing other than these and Short, Usage, Help, Default, Terminates, RunAfter.
442 func (r *Trigger) Validate() error {
443 444 R := *r
445 if len(R) < 3 {
446 return errors.New(
447 "Trigger must contain a name, Brief and Handler at minimum")
448 }
449 name, ok := R[0].(string)
450 if !ok {
451 return errors.New("first element of Trigger must be the name")
452 } else if e := ValidName(name); e != nil {
453 return fmt.Errorf("Invalid Name in Trigger at index 0: %v", e)
454 }
455 // validSet is an array that represent the presence of the mandatory parts.
456 var validSet [2]bool
457 brief, handler := 0, 1
458 var singleSet [7]bool
459 short, usage, help, defon, terminates, runafter, group := 0, 1, 2, 3, 4, 5, 6
460 for i, x := range R[1:] {
461 462 switch y := x.(type) {
463 464 case Brief:
465 if validSet[brief] {
466 return fmt.Errorf("Trigger may (only) contain one Brief, second found at index %d", i)
467 } else {
468 validSet[brief] = true
469 }
470 if e := y.Validate(); e != nil {
471 return fmt.Errorf(
472 "Trigger contains invalid element at %d - %s", i, e)
473 }
474 475 case func(*Tri) int:
476 if validSet[handler] {
477 return fmt.Errorf(
478 "Trigger may (only) contain one Handler, second found at index %d", i)
479 } else {
480 validSet[handler] = true
481 }
482 if y == nil {
483 return fmt.Errorf("Handler at index %d may not be nil", i)
484 }
485 486 case Short:
487 if singleSet[short] {
488 return fmt.Errorf("Trigger may only contain one Short, extra found at index %d", i)
489 }
490 singleSet[short] = true
491 if e := y.Validate(); e != nil {
492 return fmt.Errorf(
493 "Trigger contains invalid element at %d - %s", i, e)
494 }
495 496 case Usage:
497 if singleSet[usage] {
498 return fmt.Errorf("Trigger may only contain one Usage, extra found at index %d", i)
499 }
500 singleSet[usage] = true
501 if e := y.Validate(); e != nil {
502 return fmt.Errorf(
503 "Trigger contains invalid element at %d - %s", i, e)
504 }
505 506 case Help:
507 if singleSet[help] {
508 return fmt.Errorf("Trigger may only contain one Help, extra found at index %d", i)
509 }
510 singleSet[help] = true
511 if e := y.Validate(); e != nil {
512 return fmt.Errorf(
513 "Trigger contains invalid element at %d - %s", i, e)
514 }
515 516 case DefaultOn:
517 if singleSet[defon] {
518 return fmt.Errorf("Trigger may only contain one DefaultOn, extra found at index %d", i)
519 }
520 singleSet[defon] = true
521 if e := y.Validate(); e != nil {
522 return fmt.Errorf(
523 "Trigger contains invalid element at %d - %s", i, e)
524 }
525 526 case Terminates:
527 if singleSet[terminates] {
528 return fmt.Errorf("Trigger may only contain one Terminates, extra found at index %d", i)
529 }
530 singleSet[terminates] = true
531 if e := y.Validate(); e != nil {
532 return fmt.Errorf(
533 "Trigger contains invalid element at %d - %s", i, e)
534 }
535 536 case RunAfter:
537 if singleSet[runafter] {
538 return fmt.Errorf("Trigger may only contain one RunAfter, extra found at index %d", i)
539 }
540 singleSet[runafter] = true
541 if e := y.Validate(); e != nil {
542 return fmt.Errorf(
543 "Trigger contains invalid element at %d - %s", i, e)
544 }
545 546 case Group:
547 if singleSet[group] {
548 return fmt.Errorf(
549 "Trigger may only contain one Group, extra found at index %d", i)
550 }
551 singleSet[group] = true
552 if e := y.Validate(); e != nil {
553 return fmt.Errorf(
554 "Trigger contains invalid element at %d - %s", i, e)
555 }
556 557 default:
558 return fmt.Errorf(
559 "found invalid item type at element %d in a Trigger", i)
560 }
561 }
562 if !(validSet[brief] && validSet[handler]) {
563 return errors.New("Trigger must contain one each of Brief and Handler")
564 }
565 return nil
566 }
567 568 // Validate checks to ensure the contents of this node type satisfy constraints.
569 // Usage fields contain only one string of no more than 80 characters and no control characters.
570 func (r *Usage) Validate() error {
571 R := *r
572 if len(R) != 1 {
573 return errors.New("Usage field must contain (only) one element")
574 }
575 s, ok := R[0].(string)
576 if !ok {
577 return errors.New("Usage field element is not a string")
578 }
579 if ll := len(s); ll > 80 {
580 return fmt.Errorf("Usage string is %d chars long, may not be longer than 80", ll)
581 }
582 for i, x := range s {
583 if unicode.IsControl(x) {
584 return fmt.Errorf(
585 "Usage field string may not contain control characters, one found at index %d", i)
586 }
587 }
588 return nil
589 }
590 591 // Validate checks to ensure the contents of this node type satisfy constraints.
592 // Var must contain name, Brief and Slot, and optionally, Short, Usage, Help and Default. The type in the Slot and the Default must be the same.
593 func (r *Var) Validate() error {
594 595 R := *r
596 if len(R) < 3 {
597 return errors.New(
598 "Var must contain a name, Brief and Slot at minimum")
599 }
600 name, ok := R[0].(string)
601 if !ok {
602 return errors.New("first element of Var must be the name")
603 } else if e := ValidName(name); e != nil {
604 return fmt.Errorf("Invalid Name in Var at index 0: %v", e)
605 }
606 // validSet is an array that represent the presence of the mandatory parts.
607 var validSet [2]bool
608 brief, slot := 0, 1
609 // singleSet is an array representing the optional elements that may not be more than one inside a Var
610 var singleSet [5]bool
611 short, usage, help, def, group := 0, 1, 2, 3, 4
612 for i, x := range R[1:] {
613 614 switch y := x.(type) {
615 616 case Brief:
617 if validSet[brief] {
618 return fmt.Errorf("Var may must (only) contain one Brief, second found at index %d", i)
619 } else {
620 validSet[brief] = true
621 }
622 if e := y.Validate(); e != nil {
623 return fmt.Errorf(
624 "Var contains invalid element at %d - %s", i, e)
625 }
626 627 case Short:
628 if singleSet[short] {
629 return fmt.Errorf("Var may only contain one Short, extra found at index %d", i)
630 }
631 singleSet[short] = true
632 if e := y.Validate(); e != nil {
633 return fmt.Errorf(
634 "Var contains invalid element at %d - %s", i, e)
635 }
636 637 case Usage:
638 if singleSet[usage] {
639 return fmt.Errorf("Var may only contain one Usage, extra found at index %d", i)
640 }
641 singleSet[usage] = true
642 if e := y.Validate(); e != nil {
643 return fmt.Errorf(
644 "Var contains invalid element at %d - %s", i, e)
645 }
646 647 case Help:
648 if singleSet[help] {
649 return fmt.Errorf("Var may only contain one Help, extra found at index %d", i)
650 }
651 singleSet[help] = true
652 if e := y.Validate(); e != nil {
653 return fmt.Errorf(
654 "Var contains invalid element at %d - %s", i, e)
655 }
656 657 case Default:
658 if singleSet[def] {
659 return fmt.Errorf("Var may only contain one Default, extra found at index %d", i)
660 }
661 singleSet[def] = true
662 if e := y.Validate(); e != nil {
663 return fmt.Errorf(
664 "Var contains invalid element at %d - %s", i, e)
665 }
666 for _, z := range R {
667 s, ok := z.(Slot)
668 if ok {
669 switch s[0].(type) {
670 case *string:
671 _, ok := y[0].(string)
672 if !ok {
673 return errors.New("slot is not same type as default")
674 }
675 case *int:
676 _, ok := y[0].(int)
677 if !ok {
678 return errors.New("slot is not same type as default")
679 }
680 case *uint32:
681 _, ok := y[0].(uint32)
682 if !ok {
683 return errors.New("slot is not same type as default")
684 }
685 case *float64:
686 _, ok := y[0].(float64)
687 if !ok {
688 return errors.New("slot is not same type as default")
689 }
690 case *[]string:
691 _, ok := y[0].([]string)
692 if !ok {
693 return errors.New("slot is not same type as default")
694 }
695 case *time.Duration:
696 _, ok := y[0].(time.Duration)
697 if !ok {
698 return errors.New("slot is not same type as default")
699 }
700 }
701 // *s[0] = *y[0]
702 }
703 }
704 705 case Slot:
706 if validSet[slot] {
707 return fmt.Errorf("Var may only contain one Slot, extra found at index %d", i)
708 }
709 validSet[slot] = true
710 if e := y.Validate(); e != nil {
711 return fmt.Errorf(
712 "Var contains invalid element at %d - %s", i, e)
713 }
714 715 case Group:
716 if singleSet[group] {
717 return fmt.Errorf(
718 "Var may only contain one Group, extra found at index %d", i)
719 }
720 singleSet[group] = true
721 if e := y.Validate(); e != nil {
722 return fmt.Errorf(
723 "Var contains invalid element at %d - %s", i, e)
724 }
725 default:
726 return fmt.Errorf(
727 "found invalid item type at element %d in a Var", i)
728 }
729 }
730 if !(validSet[brief] && validSet[slot]) {
731 return errors.New("Var must contain one each of Brief and Slot")
732 }
733 // TODO: check that Default value can be assigned to dereferenced Slot variable
734 735 return nil
736 }
737 738 // Validate checks to ensure the contents of this node type satisfy constraints.
739 // A version item contains three integers and an optional (less than 16 character) string, and the numbers may not be more than 99.
740 func (r *Version) Validate() error {
741 742 R := *r
743 if len(R) > 4 {
744 return errors.New("Version field may not contain more than 4 fields")
745 }
746 if len(R) < 3 {
747 return errors.New("Version field must contain at least 3 fields")
748 }
749 for i, x := range R[:3] {
750 n, ok := x.(int)
751 if !ok {
752 return fmt.Errorf("Version field %d is not an integer: %d", i, n)
753 }
754 if n > 99 {
755 return fmt.Errorf("Version field %d value is over 99: %d", i, n)
756 }
757 }
758 if len(R) > 3 {
759 s, ok := R[3].(string)
760 if !ok {
761 return fmt.Errorf("optional field 4 of Version is not a string")
762 } else {
763 for i, x := range s {
764 if !(unicode.IsLetter(x) || unicode.IsDigit(x)) {
765 return fmt.Errorf(
766 "optional field 4 of Version contains other than letters and numbers at position %d: '%v,", i, x)
767 }
768 }
769 }
770 }
771 return nil
772 }
773 774 // ValidName checks that a Tri name element that should be a name only contains letters.
775 func ValidName(s string) error {
776 777 if len(s) < 3 {
778 return errors.New("name is less than 3 characters long")
779 }
780 for i, x := range s {
781 if !unicode.IsLetter(x) {
782 return fmt.Errorf(
783 "element %d, '%v' of name is not a letter", i, x)
784 }
785 }
786 return nil
787 }
788