github.com/hashicorp/terraform-plugin-sdk@v1.17.2/helper/validation/strings.go (about)

     1  package validation
     2  
     3  import (
     4  	"encoding/base64"
     5  	"fmt"
     6  	"regexp"
     7  	"strings"
     8  
     9  	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
    10  	"github.com/hashicorp/terraform-plugin-sdk/helper/structure"
    11  )
    12  
    13  // StringIsNotEmpty is a ValidateFunc that ensures a string is not empty
    14  func StringIsNotEmpty(i interface{}, k string) ([]string, []error) {
    15  	v, ok := i.(string)
    16  	if !ok {
    17  		return nil, []error{fmt.Errorf("expected type of %q to be string", k)}
    18  	}
    19  
    20  	if v == "" {
    21  		return nil, []error{fmt.Errorf("expected %q to not be an empty string, got %v", k, i)}
    22  	}
    23  
    24  	return nil, nil
    25  }
    26  
    27  // StringIsNotWhiteSpace is a ValidateFunc that ensures a string is not empty or consisting entirely of whitespace characters
    28  func StringIsNotWhiteSpace(i interface{}, k string) ([]string, []error) {
    29  	v, ok := i.(string)
    30  	if !ok {
    31  		return nil, []error{fmt.Errorf("expected type of %q to be string", k)}
    32  	}
    33  
    34  	if strings.TrimSpace(v) == "" {
    35  		return nil, []error{fmt.Errorf("expected %q to not be an empty string or whitespace", k)}
    36  	}
    37  
    38  	return nil, nil
    39  }
    40  
    41  // StringIsEmpty is a ValidateFunc that ensures a string has no characters
    42  func StringIsEmpty(i interface{}, k string) ([]string, []error) {
    43  	v, ok := i.(string)
    44  	if !ok {
    45  		return nil, []error{fmt.Errorf("expected type of %q to be string", k)}
    46  	}
    47  
    48  	if v != "" {
    49  		return nil, []error{fmt.Errorf("expected %q to be an empty string: got %v", k, v)}
    50  	}
    51  
    52  	return nil, nil
    53  }
    54  
    55  // StringIsWhiteSpace is a ValidateFunc that ensures a string is composed of entirely whitespace
    56  func StringIsWhiteSpace(i interface{}, k string) ([]string, []error) {
    57  	v, ok := i.(string)
    58  	if !ok {
    59  		return nil, []error{fmt.Errorf("expected type of %q to be string", k)}
    60  	}
    61  
    62  	if strings.TrimSpace(v) != "" {
    63  		return nil, []error{fmt.Errorf("expected %q to be an empty string or whitespace: got %v", k, v)}
    64  	}
    65  
    66  	return nil, nil
    67  }
    68  
    69  // StringLenBetween returns a SchemaValidateFunc which tests if the provided value
    70  // is of type string and has length between min and max (inclusive)
    71  func StringLenBetween(min, max int) schema.SchemaValidateFunc {
    72  	return func(i interface{}, k string) (warnings []string, errors []error) {
    73  		v, ok := i.(string)
    74  		if !ok {
    75  			errors = append(errors, fmt.Errorf("expected type of %s to be string", k))
    76  			return warnings, errors
    77  		}
    78  
    79  		if len(v) < min || len(v) > max {
    80  			errors = append(errors, fmt.Errorf("expected length of %s to be in the range (%d - %d), got %s", k, min, max, v))
    81  		}
    82  
    83  		return warnings, errors
    84  	}
    85  }
    86  
    87  // StringMatch returns a SchemaValidateFunc which tests if the provided value
    88  // matches a given regexp. Optionally an error message can be provided to
    89  // return something friendlier than "must match some globby regexp".
    90  func StringMatch(r *regexp.Regexp, message string) schema.SchemaValidateFunc {
    91  	return func(i interface{}, k string) ([]string, []error) {
    92  		v, ok := i.(string)
    93  		if !ok {
    94  			return nil, []error{fmt.Errorf("expected type of %s to be string", k)}
    95  		}
    96  
    97  		if ok := r.MatchString(v); !ok {
    98  			if message != "" {
    99  				return nil, []error{fmt.Errorf("invalid value for %s (%s)", k, message)}
   100  
   101  			}
   102  			return nil, []error{fmt.Errorf("expected value of %s to match regular expression %q, got %v", k, r, i)}
   103  		}
   104  		return nil, nil
   105  	}
   106  }
   107  
   108  // StringDoesNotMatch returns a SchemaValidateFunc which tests if the provided value
   109  // does not match a given regexp. Optionally an error message can be provided to
   110  // return something friendlier than "must not match some globby regexp".
   111  func StringDoesNotMatch(r *regexp.Regexp, message string) schema.SchemaValidateFunc {
   112  	return func(i interface{}, k string) ([]string, []error) {
   113  		v, ok := i.(string)
   114  		if !ok {
   115  			return nil, []error{fmt.Errorf("expected type of %s to be string", k)}
   116  		}
   117  
   118  		if ok := r.MatchString(v); ok {
   119  			if message != "" {
   120  				return nil, []error{fmt.Errorf("invalid value for %s (%s)", k, message)}
   121  
   122  			}
   123  			return nil, []error{fmt.Errorf("expected value of %s to not match regular expression %q, got %v", k, r, i)}
   124  		}
   125  		return nil, nil
   126  	}
   127  }
   128  
   129  // StringInSlice returns a SchemaValidateFunc which tests if the provided value
   130  // is of type string and matches the value of an element in the valid slice
   131  // will test with in lower case if ignoreCase is true
   132  func StringInSlice(valid []string, ignoreCase bool) schema.SchemaValidateFunc {
   133  	return func(i interface{}, k string) (warnings []string, errors []error) {
   134  		v, ok := i.(string)
   135  		if !ok {
   136  			errors = append(errors, fmt.Errorf("expected type of %s to be string", k))
   137  			return warnings, errors
   138  		}
   139  
   140  		for _, str := range valid {
   141  			if v == str || (ignoreCase && strings.ToLower(v) == strings.ToLower(str)) {
   142  				return warnings, errors
   143  			}
   144  		}
   145  
   146  		errors = append(errors, fmt.Errorf("expected %s to be one of %v, got %s", k, valid, v))
   147  		return warnings, errors
   148  	}
   149  }
   150  
   151  // StringNotInSlice returns a SchemaValidateFunc which tests if the provided value
   152  // is of type string and does not match the value of any element in the invalid slice
   153  // will test with in lower case if ignoreCase is true
   154  func StringNotInSlice(invalid []string, ignoreCase bool) schema.SchemaValidateFunc {
   155  	return func(i interface{}, k string) (warnings []string, errors []error) {
   156  		v, ok := i.(string)
   157  		if !ok {
   158  			errors = append(errors, fmt.Errorf("expected type of %s to be string", k))
   159  			return warnings, errors
   160  		}
   161  
   162  		for _, str := range invalid {
   163  			if v == str || (ignoreCase && strings.ToLower(v) == strings.ToLower(str)) {
   164  				errors = append(errors, fmt.Errorf("expected %s to not be any of %v, got %s", k, invalid, v))
   165  				return warnings, errors
   166  			}
   167  		}
   168  
   169  		return warnings, errors
   170  	}
   171  }
   172  
   173  // StringDoesNotContainAny returns a SchemaValidateFunc which validates that the
   174  // provided value does not contain any of the specified Unicode code points in chars.
   175  func StringDoesNotContainAny(chars string) schema.SchemaValidateFunc {
   176  	return func(i interface{}, k string) (warnings []string, errors []error) {
   177  		v, ok := i.(string)
   178  		if !ok {
   179  			errors = append(errors, fmt.Errorf("expected type of %s to be string", k))
   180  			return warnings, errors
   181  		}
   182  
   183  		if strings.ContainsAny(v, chars) {
   184  			errors = append(errors, fmt.Errorf("expected value of %s to not contain any of %q, got %v", k, chars, i))
   185  			return warnings, errors
   186  		}
   187  
   188  		return warnings, errors
   189  	}
   190  }
   191  
   192  // StringIsBase64 is a ValidateFunc that ensures a string can be parsed as Base64
   193  func StringIsBase64(i interface{}, k string) (warnings []string, errors []error) {
   194  	// Empty string is not allowed
   195  	if warnings, errors = StringIsNotEmpty(i, k); len(errors) > 0 {
   196  		return
   197  	}
   198  
   199  	// NoEmptyStrings checks it is a string
   200  	v, _ := i.(string)
   201  
   202  	if _, err := base64.StdEncoding.DecodeString(v); err != nil {
   203  		errors = append(errors, fmt.Errorf("expected %q to be a base64 string, got %v", k, v))
   204  	}
   205  
   206  	return warnings, errors
   207  }
   208  
   209  // ValidateJsonString is a SchemaValidateFunc which tests to make sure the
   210  // supplied string is valid JSON.
   211  //
   212  // Deprecated: use StringIsJSON instead
   213  func ValidateJsonString(i interface{}, k string) (warnings []string, errors []error) {
   214  	return StringIsJSON(i, k)
   215  }
   216  
   217  // StringIsJSON is a SchemaValidateFunc which tests to make sure the supplied string is valid JSON.
   218  func StringIsJSON(i interface{}, k string) (warnings []string, errors []error) {
   219  	v, ok := i.(string)
   220  	if !ok {
   221  		errors = append(errors, fmt.Errorf("expected type of %s to be string", k))
   222  		return warnings, errors
   223  	}
   224  
   225  	if _, err := structure.NormalizeJsonString(v); err != nil {
   226  		errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err))
   227  	}
   228  
   229  	return warnings, errors
   230  }
   231  
   232  // ValidateRegexp returns a SchemaValidateFunc which tests to make sure the
   233  // supplied string is a valid regular expression.
   234  //
   235  // Deprecated: use StringIsValidRegExp instead
   236  func ValidateRegexp(i interface{}, k string) (warnings []string, errors []error) {
   237  	return StringIsValidRegExp(i, k)
   238  }
   239  
   240  // StringIsValidRegExp returns a SchemaValidateFunc which tests to make sure the supplied string is a valid regular expression.
   241  func StringIsValidRegExp(i interface{}, k string) (warnings []string, errors []error) {
   242  	v, ok := i.(string)
   243  	if !ok {
   244  		errors = append(errors, fmt.Errorf("expected type of %s to be string", k))
   245  		return warnings, errors
   246  	}
   247  
   248  	if _, err := regexp.Compile(v); err != nil {
   249  		errors = append(errors, fmt.Errorf("%q: %s", k, err))
   250  	}
   251  
   252  	return warnings, errors
   253  }