github.com/elves/elvish@v0.15.0/pkg/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("concat not implemented") 24 25 type cannotConcat struct { 26 lhsKind string 27 rhsKind string 28 } 29 30 func (err cannotConcat) Error() string { 31 return fmt.Sprintf("cannot concatenate %s and %s", err.lhsKind, err.rhsKind) 32 } 33 34 // Concat concatenates two values. If both operands are strings, it returns lhs 35 // + rhs, nil. If the left operand implements Concatter, it calls 36 // lhs.Concat(rhs). If lhs doesn't implement the interface or returned 37 // ErrConcatNotImplemented, it then calls rhs.RConcat(lhs). If all attempts 38 // fail, it returns nil and an error. 39 func Concat(lhs, rhs interface{}) (interface{}, error) { 40 if v, ok := tryConcatBuiltins(lhs, rhs); ok { 41 return v, nil 42 } 43 44 if lhs, ok := lhs.(Concatter); ok { 45 v, err := lhs.Concat(rhs) 46 if err != ErrConcatNotImplemented { 47 return v, err 48 } 49 } 50 51 if rhs, ok := rhs.(RConcatter); ok { 52 v, err := rhs.RConcat(lhs) 53 if err != ErrConcatNotImplemented { 54 return v, err 55 } 56 } 57 58 return nil, cannotConcat{Kind(lhs), Kind(rhs)} 59 } 60 61 func tryConcatBuiltins(lhs, rhs interface{}) (interface{}, bool) { 62 switch lhs := lhs.(type) { 63 case string, float64: 64 switch rhs := rhs.(type) { 65 case string, float64: 66 return ToString(lhs) + ToString(rhs), true 67 } 68 } 69 70 return nil, false 71 }