github.com/unigraph-dev/dgraph@v1.1.1-0.20200923154953-8b52b426f765/types/value.go (about)

     1  /*
     2   * Copyright 2018 Dgraph Labs, Inc. and Contributors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package types
    18  
    19  import (
    20  	"strconv"
    21  	"strings"
    22  )
    23  
    24  // TypeForValue tries to determine the most likely type based on a value. We only want to use this
    25  // function when there's no schema type and no suggested storage type.
    26  // Returns the guessed type or DefaultID if it couldn't be determined.
    27  // If retval is non-nil, the parsed value is returned, useful in conjunction with ObjectValue().
    28  func TypeForValue(v []byte) (TypeID, interface{}) {
    29  	s := string(v)
    30  	switch {
    31  	case v == nil || s == "":
    32  		break
    33  
    34  	// Possible boolean. Specific to "true" or "false".
    35  	case s[0] == 't', s[0] == 'T', s[0] == 'f', s[0] == 'F':
    36  		var b bool
    37  		// XXX: we dont use ParseBool here because it considers 't' and 'f' as values.
    38  		switch s {
    39  		case "true", "TRUE", "True":
    40  			b = true
    41  			return BoolID, b
    42  		case "false", "FALSE", "False":
    43  			return BoolID, b
    44  		}
    45  
    46  	// Possible datetime. Unfortunately, year-only value will fallthrough as int.
    47  	case checkDateTime(s):
    48  		if t, err := ParseTime(s); err == nil {
    49  			return DateTimeID, t
    50  		}
    51  
    52  	// Possible int.
    53  	case checkInt(s):
    54  		if i, err := strconv.ParseInt(s, 10, 64); err == nil {
    55  			return IntID, i
    56  		}
    57  
    58  	// Possible float.
    59  	case checkFloat(s):
    60  		if f, err := strconv.ParseFloat(s, 64); err == nil {
    61  			return FloatID, f
    62  		}
    63  	}
    64  	return DefaultID, nil
    65  }
    66  
    67  func isSign(d byte) bool {
    68  	return d == '-' || d == '+'
    69  }
    70  
    71  func isDigit(d byte) bool {
    72  	return d >= '0' && d <= '9'
    73  }
    74  
    75  func checkInt(s string) bool {
    76  	if isSign(s[0]) && len(s) > 1 {
    77  		s = s[1:]
    78  	}
    79  	return isDigit(s[0]) && !strings.ContainsAny(s[1:], ".Ee")
    80  }
    81  
    82  func checkFloat(s string) bool {
    83  	if isSign(s[0]) && len(s) > 1 {
    84  		s = s[1:]
    85  	}
    86  	if s[0] == '.' && len(s) > 1 {
    87  		// .012 is totally legit
    88  		return isDigit(s[1])
    89  	}
    90  	return isDigit(s[0]) && strings.ContainsAny(s[1:], ".Ee")
    91  }
    92  
    93  func checkDateTime(s string) bool {
    94  	if len(s) < 5 {
    95  		return false
    96  	}
    97  	return isDigit(s[0]) && isDigit(s[1]) && isDigit(s[2]) && isDigit(s[3]) && s[4] == '-'
    98  }