github.com/42z-io/confik@v0.0.2-0.20231103050132-21d8f377356c/tag.go (about)

     1  package confik
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  )
     7  
     8  type tag struct {
     9  	Name     string
    10  	Flags    []string
    11  	Settings map[string]string
    12  }
    13  
    14  // ConfigTag represents the name, flags and settings on the struct field.
    15  type ConfigTag struct {
    16  	Name      string  // name of the environment variable
    17  	Validator *string // field validator name
    18  	Optional  bool    // is the environment variable optional?
    19  	Default   *string // default value to use if the environment variable does not exist
    20  	Unset     bool    // clear the environment variable after load?
    21  }
    22  
    23  // NewConfigTag will create a new [ConfigTag] with the default values.
    24  func NewConfigTag(name string) ConfigTag {
    25  	return ConfigTag{
    26  		Name:      name,
    27  		Validator: nil,
    28  		Optional:  false,
    29  		Default:   nil,
    30  		Unset:     false,
    31  	}
    32  }
    33  
    34  func parseSetting(expressionStr string) (string, string, error) {
    35  	parts := strings.Split(expressionStr, "=")
    36  	if len(parts) != 2 {
    37  		return "", "", fmt.Errorf("invalid setting %s: invalid syntax", expressionStr)
    38  	}
    39  	return parts[0], parts[1], nil
    40  }
    41  
    42  func parseTag(tagStr string) (*tag, error) {
    43  	if len(tagStr) == 0 {
    44  		return nil, fmt.Errorf("invalid tag: empty tag")
    45  	}
    46  	parts := strings.Split(tagStr, ",")
    47  	var tag tag
    48  	tag.Name = parts[0]
    49  	tag.Settings = make(map[string]string)
    50  	tag.Flags = make([]string, 0)
    51  	for _, expressionStr := range parts[1:] {
    52  		if strings.Contains(expressionStr, "=") {
    53  			settingName, settingValue, err := parseSetting(expressionStr)
    54  			if err != nil {
    55  				return nil, fmt.Errorf("invalid tag: %w", err)
    56  			}
    57  			tag.Settings[settingName] = settingValue
    58  		} else {
    59  			tag.Flags = append(tag.Flags, expressionStr)
    60  		}
    61  	}
    62  	return &tag, nil
    63  
    64  }
    65  
    66  func parseEnvTag(tagStr string) (*ConfigTag, error) {
    67  	tag, err := parseTag(tagStr)
    68  	if err != nil {
    69  		return nil, fmt.Errorf("invalid env tag: %w", err)
    70  	}
    71  
    72  	if err := verifyEnvName(tag.Name); err != nil {
    73  		return nil, fmt.Errorf("invalid env tag: %w", err)
    74  	}
    75  
    76  	configTag := NewConfigTag(tag.Name)
    77  	for _, flagName := range tag.Flags {
    78  		switch flagName {
    79  		case "optional":
    80  			configTag.Optional = true
    81  		case "unset":
    82  			configTag.Unset = true
    83  		default:
    84  			return nil, fmt.Errorf("invalid env tag: unknown flag %s", flagName)
    85  		}
    86  	}
    87  
    88  	for settingName, settingValue := range tag.Settings {
    89  		// TODO remove in Go 1.22
    90  		settingValue := settingValue
    91  		switch settingName {
    92  		case "validate":
    93  			configTag.Validator = &settingValue
    94  		case "default":
    95  			configTag.Default = &settingValue
    96  		default:
    97  			return nil, fmt.Errorf("invalid env tag: unknown setting %s", settingName)
    98  		}
    99  	}
   100  
   101  	return &configTag, nil
   102  }