forked from tangled.org/core
Monorepo for Tangled — https://tangled.org
at master 1.3 kB view raw
1package validator 2 3import ( 4 "fmt" 5 "maps" 6 "regexp" 7 "slices" 8 "strings" 9) 10 11const ( 12 maxTopicLen = 50 13 maxTopics = 20 14) 15 16var ( 17 topicRE = regexp.MustCompile(`\A[a-z0-9-]+\z`) 18) 19 20// ValidateRepoTopicStr parses and validates whitespace-separated topic string. 21// 22// Rules: 23// - topics are separated by whitespace 24// - each topic may contain lowercase letters, digits, and hyphens only 25// - each topic must be <= 50 characters long 26// - no more than 20 topics allowed 27// - duplicates are removed 28func (v *Validator) ValidateRepoTopicStr(topicsStr string) ([]string, error) { 29 topicsStr = strings.TrimSpace(topicsStr) 30 if topicsStr == "" { 31 return nil, nil 32 } 33 parts := strings.Fields(topicsStr) 34 if len(parts) > maxTopics { 35 return nil, fmt.Errorf("too many topics: %d (maximum %d)", len(parts), maxTopics) 36 } 37 38 topicSet := make(map[string]struct{}) 39 40 for _, t := range parts { 41 if _, exists := topicSet[t]; exists { 42 continue 43 } 44 if len(t) > maxTopicLen { 45 return nil, fmt.Errorf("topic '%s' is too long (maximum %d characters)", t, maxTopics) 46 } 47 if !topicRE.MatchString(t) { 48 return nil, fmt.Errorf("topic '%s' contains invalid characters (allowed: lowercase letters, digits, hyphens)", t) 49 } 50 topicSet[t] = struct{}{} 51 } 52 return slices.Collect(maps.Keys(topicSet)), nil 53}