github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/cmds/core/elvish/eval/vals/concat.go (about) 1 package vals 2 3 import ( 4 "errors" 5 "fmt" 6 ) 7 8 // Concatter wraps the Concat method. See Concat for how it is used. 9 type Concatter interface { 10 // Concat concatenates the receiver with another value, the receiver being 11 // the left operand. If concatenation is not supported for the given value, 12 // the method can return the special error type ErrCatNotImplemented. 13 Concat(v interface{}) (interface{}, error) 14 } 15 16 // RConcatter wraps the RConcat method. See Concat for how it is used. 17 type RConcatter interface { 18 RConcat(v interface{}) (interface{}, error) 19 } 20 21 // ErrConcatNotImplemented is a special error value used to signal that 22 // concatenation is not implemented. See Concat for how it is used. 23 var ErrConcatNotImplemented = errors.New("cat not implemented") 24 25 // Concat concatenates two values. If both operands are strings, it returns lhs 26 // + rhs, nil. If the left operand implements Concatter, it calls 27 // lhs.Concat(rhs). If lhs doesn't implement the interface or returned 28 // ErrConcatNotImplemented, it then calls rhs.RConcat(lhs). If all attempts 29 // fail, it returns nil and an error. 30 func Concat(lhs, rhs interface{}) (interface{}, error) { 31 if v, ok := tryConcatBuiltins(lhs, rhs); ok { 32 return v, nil 33 } 34 35 if lhs, ok := lhs.(Concatter); ok { 36 v, err := lhs.Concat(rhs) 37 if err != ErrConcatNotImplemented { 38 return v, err 39 } 40 } 41 42 if rhs, ok := rhs.(RConcatter); ok { 43 v, err := rhs.RConcat(lhs) 44 if err != ErrConcatNotImplemented { 45 return v, err 46 } 47 } 48 49 return nil, fmt.Errorf("unsupported concat: %s and %s", Kind(lhs), Kind(rhs)) 50 } 51 52 func tryConcatBuiltins(lhs, rhs interface{}) (interface{}, bool) { 53 switch lhs := lhs.(type) { 54 case string: 55 if rhs, ok := rhs.(string); ok { 56 return lhs + rhs, true 57 } 58 } 59 60 return nil, false 61 }