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  }