gopkg.in/rethinkdb/rethinkdb-go.v6@v6.2.2/utils.go (about)

     1  package rethinkdb
     2  
     3  import (
     4  	"reflect"
     5  	"strconv"
     6  	"strings"
     7  	"sync/atomic"
     8  
     9  	"gopkg.in/rethinkdb/rethinkdb-go.v6/encoding"
    10  	p "gopkg.in/rethinkdb/rethinkdb-go.v6/ql2"
    11  )
    12  
    13  // Helper functions for constructing terms
    14  
    15  // constructRootTerm is an alias for creating a new term.
    16  func constructRootTerm(name string, termType p.Term_TermType, args []interface{}, optArgs map[string]interface{}) Term {
    17  	return Term{
    18  		name:     name,
    19  		rootTerm: true,
    20  		termType: termType,
    21  		args:     convertTermList(args),
    22  		optArgs:  convertTermObj(optArgs),
    23  	}
    24  }
    25  
    26  // constructMethodTerm is an alias for creating a new term. Unlike constructRootTerm
    27  // this function adds the previous expression in the tree to the argument list to
    28  // create a method term.
    29  func constructMethodTerm(prevVal Term, name string, termType p.Term_TermType, args []interface{}, optArgs map[string]interface{}) Term {
    30  	args = append([]interface{}{prevVal}, args...)
    31  
    32  	return Term{
    33  		name:     name,
    34  		rootTerm: false,
    35  		termType: termType,
    36  		args:     convertTermList(args),
    37  		optArgs:  convertTermObj(optArgs),
    38  	}
    39  }
    40  
    41  // Helper functions for creating internal RQL types
    42  
    43  func newQuery(t Term, qopts map[string]interface{}, copts *ConnectOpts) (q Query, err error) {
    44  	queryOpts := map[string]interface{}{}
    45  	for k, v := range qopts {
    46  		queryOpts[k], err = Expr(v).Build()
    47  		if err != nil {
    48  			return
    49  		}
    50  	}
    51  	if copts.Database != "" {
    52  		queryOpts["db"], err = DB(copts.Database).Build()
    53  		if err != nil {
    54  			return
    55  		}
    56  	}
    57  
    58  	builtTerm, err := t.Build()
    59  	if err != nil {
    60  		return q, err
    61  	}
    62  
    63  	// Construct query
    64  	return Query{
    65  		Type:      p.Query_START,
    66  		Term:      &t,
    67  		Opts:      queryOpts,
    68  		builtTerm: builtTerm,
    69  	}, nil
    70  }
    71  
    72  // makeArray takes a slice of terms and produces a single MAKE_ARRAY term
    73  func makeArray(args termsList) Term {
    74  	return Term{
    75  		name:     "[...]",
    76  		termType: p.Term_MAKE_ARRAY,
    77  		args:     args,
    78  	}
    79  }
    80  
    81  // makeObject takes a map of terms and produces a single MAKE_OBJECT term
    82  func makeObject(args termsObj) Term {
    83  	return Term{
    84  		name:     "{...}",
    85  		termType: p.Term_MAKE_OBJ,
    86  		optArgs:  args,
    87  	}
    88  }
    89  
    90  var nextVarID int64
    91  
    92  func makeFunc(f interface{}) Term {
    93  	value := reflect.ValueOf(f)
    94  	valueType := value.Type()
    95  
    96  	var argNums = make([]interface{}, valueType.NumIn())
    97  	var args = make([]reflect.Value, valueType.NumIn())
    98  	for i := 0; i < valueType.NumIn(); i++ {
    99  		// Get a slice of the VARs to use as the function arguments
   100  		varID := atomic.AddInt64(&nextVarID, 1)
   101  		args[i] = reflect.ValueOf(constructRootTerm("var", p.Term_VAR, []interface{}{varID}, map[string]interface{}{}))
   102  		argNums[i] = varID
   103  
   104  		// make sure all input arguments are of type Term
   105  		argValueTypeName := valueType.In(i).String()
   106  		if argValueTypeName != "rethinkdb.Term" && argValueTypeName != "interface {}" {
   107  			panic("Function argument is not of type Term or interface {}")
   108  		}
   109  	}
   110  
   111  	if valueType.NumOut() != 1 {
   112  		panic("Function does not have a single return value")
   113  	}
   114  
   115  	body := value.Call(args)[0].Interface()
   116  	argsArr := makeArray(convertTermList(argNums))
   117  
   118  	return constructRootTerm("func", p.Term_FUNC, []interface{}{argsArr, body}, map[string]interface{}{})
   119  }
   120  
   121  func funcWrap(value interface{}) Term {
   122  	val := Expr(value)
   123  
   124  	if implVarScan(val) && val.termType != p.Term_ARGS {
   125  		return makeFunc(func(x Term) Term {
   126  			return val
   127  		})
   128  	}
   129  	return val
   130  }
   131  
   132  func funcWrapArgs(args []interface{}) []interface{} {
   133  	for i, arg := range args {
   134  		args[i] = funcWrap(arg)
   135  	}
   136  
   137  	return args
   138  }
   139  
   140  // implVarScan recursivly checks a value to see if it contains an
   141  // IMPLICIT_VAR term. If it does it returns true
   142  func implVarScan(value Term) bool {
   143  	if value.termType == p.Term_IMPLICIT_VAR {
   144  		return true
   145  	}
   146  	for _, v := range value.args {
   147  		if implVarScan(v) {
   148  			return true
   149  		}
   150  	}
   151  
   152  	for _, v := range value.optArgs {
   153  		if implVarScan(v) {
   154  			return true
   155  		}
   156  	}
   157  
   158  	return false
   159  }
   160  
   161  // Convert an opt args struct to a map.
   162  func optArgsToMap(optArgs OptArgs) map[string]interface{} {
   163  	data, err := encode(optArgs)
   164  
   165  	if err == nil && data != nil {
   166  		if m, ok := data.(map[string]interface{}); ok {
   167  			return m
   168  		}
   169  	}
   170  
   171  	return map[string]interface{}{}
   172  }
   173  
   174  // Convert a list into a slice of terms
   175  func convertTermList(l []interface{}) termsList {
   176  	if len(l) == 0 {
   177  		return nil
   178  	}
   179  
   180  	terms := make(termsList, len(l))
   181  	for i, v := range l {
   182  		terms[i] = Expr(v)
   183  	}
   184  
   185  	return terms
   186  }
   187  
   188  // Convert a map into a map of terms
   189  func convertTermObj(o map[string]interface{}) termsObj {
   190  	if len(o) == 0 {
   191  		return nil
   192  	}
   193  
   194  	terms := make(termsObj, len(o))
   195  	for k, v := range o {
   196  		terms[k] = Expr(v)
   197  	}
   198  
   199  	return terms
   200  }
   201  
   202  // Helper functions for debugging
   203  
   204  func allArgsToStringSlice(args termsList, optArgs termsObj) []string {
   205  	allArgs := make([]string, len(args)+len(optArgs))
   206  	i := 0
   207  
   208  	for _, v := range args {
   209  		allArgs[i] = v.String()
   210  		i++
   211  	}
   212  	for k, v := range optArgs {
   213  		allArgs[i] = k + "=" + v.String()
   214  		i++
   215  	}
   216  
   217  	return allArgs
   218  }
   219  
   220  func argsToStringSlice(args termsList) []string {
   221  	allArgs := make([]string, len(args))
   222  
   223  	for i, v := range args {
   224  		allArgs[i] = v.String()
   225  	}
   226  
   227  	return allArgs
   228  }
   229  
   230  func optArgsToStringSlice(optArgs termsObj) []string {
   231  	allArgs := make([]string, len(optArgs))
   232  	i := 0
   233  
   234  	for k, v := range optArgs {
   235  		allArgs[i] = k + "=" + v.String()
   236  		i++
   237  	}
   238  
   239  	return allArgs
   240  }
   241  
   242  func splitAddress(address string) (hostname string, port int) {
   243  	hostname = "localhost"
   244  	port = 28015
   245  
   246  	addrParts := strings.Split(address, ":")
   247  
   248  	if len(addrParts) >= 1 {
   249  		hostname = addrParts[0]
   250  	}
   251  	if len(addrParts) >= 2 {
   252  		port, _ = strconv.Atoi(addrParts[1])
   253  	}
   254  
   255  	return
   256  }
   257  
   258  func encode(data interface{}) (interface{}, error) {
   259  	if _, ok := data.(Term); ok {
   260  		return data, nil
   261  	}
   262  
   263  	v, err := encoding.Encode(data)
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  
   268  	return v, nil
   269  }
   270  
   271  // shouldRetryQuery checks the result of a query and returns true if the query
   272  // should be retried
   273  func shouldRetryQuery(q Query, err error) bool {
   274  	if err == nil {
   275  		return false
   276  	}
   277  
   278  	if _, ok := err.(RQLConnectionError); ok {
   279  		return true
   280  	}
   281  
   282  	return err == ErrConnectionClosed
   283  }