github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/eval/vals/concat.go (about)

     1  package vals
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"math/big"
     7  )
     8  
     9  // Concatter wraps the Concat method. See Concat for how it is used.
    10  type Concatter interface {
    11  	// Concat concatenates the receiver with another value, the receiver being
    12  	// the left operand. If concatenation is not supported for the given value,
    13  	// the method can return the special error type ErrCatNotImplemented.
    14  	Concat(v interface{}) (interface{}, error)
    15  }
    16  
    17  // RConcatter wraps the RConcat method. See Concat for how it is used.
    18  type RConcatter interface {
    19  	RConcat(v interface{}) (interface{}, error)
    20  }
    21  
    22  // ErrConcatNotImplemented is a special error value used to signal that
    23  // concatenation is not implemented. See Concat for how it is used.
    24  var ErrConcatNotImplemented = errors.New("concat not implemented")
    25  
    26  type cannotConcat struct {
    27  	lhsKind string
    28  	rhsKind string
    29  }
    30  
    31  func (err cannotConcat) Error() string {
    32  	return fmt.Sprintf("cannot concatenate %s and %s", err.lhsKind, err.rhsKind)
    33  }
    34  
    35  // Concat concatenates two values. If both operands are strings, it returns lhs
    36  // + rhs, nil. If the left operand implements Concatter, it calls
    37  // lhs.Concat(rhs). If lhs doesn't implement the interface or returned
    38  // ErrConcatNotImplemented, it then calls rhs.RConcat(lhs). If all attempts
    39  // fail, it returns nil and an error.
    40  func Concat(lhs, rhs interface{}) (interface{}, error) {
    41  	if v, ok := tryConcatBuiltins(lhs, rhs); ok {
    42  		return v, nil
    43  	}
    44  
    45  	if lhs, ok := lhs.(Concatter); ok {
    46  		v, err := lhs.Concat(rhs)
    47  		if err != ErrConcatNotImplemented {
    48  			return v, err
    49  		}
    50  	}
    51  
    52  	if rhs, ok := rhs.(RConcatter); ok {
    53  		v, err := rhs.RConcat(lhs)
    54  		if err != ErrConcatNotImplemented {
    55  			return v, err
    56  		}
    57  	}
    58  
    59  	return nil, cannotConcat{Kind(lhs), Kind(rhs)}
    60  }
    61  
    62  func tryConcatBuiltins(lhs, rhs interface{}) (interface{}, bool) {
    63  	switch lhs := lhs.(type) {
    64  	case string, int, *big.Int, *big.Rat, float64:
    65  		switch rhs := rhs.(type) {
    66  		case string, int, *big.Int, *big.Rat, float64:
    67  			return ToString(lhs) + ToString(rhs), true
    68  		}
    69  	}
    70  
    71  	return nil, false
    72  }