1package capability 2 3import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "strings" 8) 9 10var ( 11 // ErrArgumentsRequired is returned if no arguments are giving with a 12 // capability that requires arguments 13 ErrArgumentsRequired = errors.New("arguments required") 14 // ErrArguments is returned if arguments are given with a capabilities that 15 // not supports arguments 16 ErrArguments = errors.New("arguments not allowed") 17 // ErrEmptyArgument is returned when an empty value is given 18 ErrEmptyArgument = errors.New("empty argument") 19 // ErrMultipleArguments multiple argument given to a capabilities that not 20 // support it 21 ErrMultipleArguments = errors.New("multiple arguments not allowed") 22) 23 24// List represents a list of capabilities 25type List struct { 26 m map[Capability]*entry 27 sort []string 28} 29 30type entry struct { 31 Name Capability 32 Values []string 33} 34 35// NewList returns a new List of capabilities 36func NewList() *List { 37 return &List{ 38 m: make(map[Capability]*entry), 39 } 40} 41 42// IsEmpty returns true if the List is empty 43func (l *List) IsEmpty() bool { 44 return len(l.sort) == 0 45} 46 47// Decode decodes list of capabilities from raw into the list 48func (l *List) Decode(raw []byte) error { 49 // git 1.x receive pack used to send a leading space on its 50 // git-receive-pack capabilities announcement. We just trim space to be 51 // tolerant to space changes in different versions. 52 raw = bytes.TrimSpace(raw) 53 54 if len(raw) == 0 { 55 return nil 56 } 57 58 for _, data := range bytes.Split(raw, []byte{' '}) { 59 pair := bytes.SplitN(data, []byte{'='}, 2) 60 61 c := Capability(pair[0]) 62 if len(pair) == 1 { 63 if err := l.Add(c); err != nil { 64 return err 65 } 66 67 continue 68 } 69 70 if err := l.Add(c, string(pair[1])); err != nil { 71 return err 72 } 73 } 74 75 return nil 76} 77 78// Get returns the values for a capability 79func (l *List) Get(capability Capability) []string { 80 if _, ok := l.m[capability]; !ok { 81 return nil 82 } 83 84 return l.m[capability].Values 85} 86 87// Set sets a capability removing the previous values 88func (l *List) Set(capability Capability, values ...string) error { 89 delete(l.m, capability) 90 return l.Add(capability, values...) 91} 92 93// Add adds a capability, values are optional 94func (l *List) Add(c Capability, values ...string) error { 95 if err := l.validate(c, values); err != nil { 96 return err 97 } 98 99 if !l.Supports(c) { 100 l.m[c] = &entry{Name: c} 101 l.sort = append(l.sort, c.String()) 102 } 103 104 if len(values) == 0 { 105 return nil 106 } 107 108 if known[c] && !multipleArgument[c] && len(l.m[c].Values) > 0 { 109 return ErrMultipleArguments 110 } 111 112 l.m[c].Values = append(l.m[c].Values, values...) 113 return nil 114} 115 116func (l *List) validateNoEmptyArgs(values []string) error { 117 for _, v := range values { 118 if v == "" { 119 return ErrEmptyArgument 120 } 121 } 122 return nil 123} 124 125func (l *List) validate(c Capability, values []string) error { 126 if !known[c] { 127 return l.validateNoEmptyArgs(values) 128 } 129 if requiresArgument[c] && len(values) == 0 { 130 return ErrArgumentsRequired 131 } 132 133 if !requiresArgument[c] && len(values) != 0 { 134 return ErrArguments 135 } 136 137 if !multipleArgument[c] && len(values) > 1 { 138 return ErrMultipleArguments 139 } 140 return l.validateNoEmptyArgs(values) 141} 142 143// Supports returns true if capability is present 144func (l *List) Supports(capability Capability) bool { 145 _, ok := l.m[capability] 146 return ok 147} 148 149// Delete deletes a capability from the List 150func (l *List) Delete(capability Capability) { 151 if !l.Supports(capability) { 152 return 153 } 154 155 delete(l.m, capability) 156 for i, c := range l.sort { 157 if c != string(capability) { 158 continue 159 } 160 161 l.sort = append(l.sort[:i], l.sort[i+1:]...) 162 return 163 } 164} 165 166// All returns a slice with all defined capabilities. 167func (l *List) All() []Capability { 168 var cs []Capability 169 for _, key := range l.sort { 170 cs = append(cs, Capability(key)) 171 } 172 173 return cs 174} 175 176// String generates the capabilities strings, the capabilities are sorted in 177// insertion order 178func (l *List) String() string { 179 var o []string 180 for _, key := range l.sort { 181 cap := l.m[Capability(key)] 182 if len(cap.Values) == 0 { 183 o = append(o, key) 184 continue 185 } 186 187 for _, value := range cap.Values { 188 o = append(o, fmt.Sprintf("%s=%s", key, value)) 189 } 190 } 191 192 return strings.Join(o, " ") 193} 194