...

Source file src/go.einride.tech/aip/resourcename/validate.go

Documentation: go.einride.tech/aip/resourcename

     1  package resourcename
     2  
     3  import (
     4  	"fmt"
     5  	"unicode"
     6  )
     7  
     8  // Validate that a resource name conforms to the restrictions outlined in AIP-122.
     9  // See: https://google.aip.dev/122
    10  func Validate(name string) error {
    11  	if name == "" {
    12  		return fmt.Errorf("empty")
    13  	}
    14  	var sc Scanner
    15  	sc.Init(name)
    16  	var i int
    17  	for sc.Scan() {
    18  		i++
    19  		segment := sc.Segment()
    20  		switch {
    21  		case segment == "":
    22  			return fmt.Errorf("segment %d is empty", i)
    23  		case segment == Wildcard:
    24  			continue
    25  		case segment.IsVariable():
    26  			return fmt.Errorf("segment '%s': valid resource names must not contain variables", sc.Segment())
    27  		case !isDomainName(string(sc.Segment())):
    28  			return fmt.Errorf("segment '%s': not a valid DNS name", sc.Segment())
    29  		}
    30  	}
    31  	if sc.Full() && !isDomainName(sc.ServiceName()) {
    32  		return fmt.Errorf("service '%s': not a valid DNS name", sc.Segment())
    33  	}
    34  	return nil
    35  }
    36  
    37  // ValidatePattern that a resource name pattern conforms to the restrictions outlined in AIP-122.
    38  // See: https://google.aip.dev/122
    39  func ValidatePattern(pattern string) error {
    40  	if pattern == "" {
    41  		return fmt.Errorf("empty")
    42  	}
    43  	var sc Scanner
    44  	sc.Init(pattern)
    45  	var i int
    46  	for sc.Scan() {
    47  		i++
    48  		segment := sc.Segment()
    49  		switch {
    50  		case segment == "":
    51  			return fmt.Errorf("segment %d is empty", i)
    52  		case segment == Wildcard:
    53  			return fmt.Errorf("segment '%d': wildcards not allowed in patterns", i)
    54  		case segment.IsVariable():
    55  			switch {
    56  			case segment.Literal() == "":
    57  				return fmt.Errorf("segment '%s': missing variable name", sc.Segment())
    58  			case !isSnakeCase(string(segment.Literal())):
    59  				return fmt.Errorf("segment '%s': must be valid snake case", sc.Segment())
    60  			}
    61  		case !isDomainName(string(sc.Segment())):
    62  			return fmt.Errorf("segment '%s': not a valid DNS name", sc.Segment())
    63  		}
    64  	}
    65  	if sc.Full() {
    66  		return fmt.Errorf("patterns can not be full resource names")
    67  	}
    68  	return nil
    69  }
    70  
    71  func isSnakeCase(s string) bool {
    72  	for i, r := range s {
    73  		if i == 0 {
    74  			if !unicode.IsLower(r) {
    75  				return false
    76  			}
    77  		} else if !(r == '_' || unicode.In(r, unicode.Lower, unicode.Digit)) {
    78  			return false
    79  		}
    80  	}
    81  	return true
    82  }
    83  

View as plain text