@@ -21,6 +21,12 @@ import (
21
21
"strings"
22
22
)
23
23
24
+ // ValidateQualifiedName checks the validity of a fully-qualified device name.
25
+ func ValidateQualifiedName (name string ) error {
26
+ _ , _ , _ , err := ParseFullyQualifiedName (name )
27
+ return err
28
+ }
29
+
24
30
// ValidateKind checks the validity of a CDI kind.
25
31
// The syntax for a device kind“ is
26
32
//
@@ -74,19 +80,19 @@ func validateVendorOrClassName(name string) error {
74
80
if name == "" {
75
81
return fmt .Errorf ("empty name" )
76
82
}
77
- if ! IsLetter (rune (name [0 ])) {
83
+ if ! isLetter (rune (name [0 ])) {
78
84
return fmt .Errorf ("%q, should start with letter" , name )
79
85
}
80
86
for _ , c := range string (name [1 : len (name )- 1 ]) {
81
87
switch {
82
- case IsAlphaNumeric (c ):
88
+ case isAlphaNumeric (c ):
83
89
case c == '_' || c == '-' || c == '.' :
84
90
default :
85
91
return fmt .Errorf ("invalid character '%c' in name %q" ,
86
92
c , name )
87
93
}
88
94
}
89
- if ! IsAlphaNumeric (rune (name [len (name )- 1 ])) {
95
+ if ! isAlphaNumeric (rune (name [len (name )- 1 ])) {
90
96
return fmt .Errorf ("%q, should end with a letter or digit" , name )
91
97
}
92
98
@@ -102,38 +108,107 @@ func ValidateDeviceName(name string) error {
102
108
if name == "" {
103
109
return fmt .Errorf ("invalid (empty) device name" )
104
110
}
105
- if ! IsAlphaNumeric (rune (name [0 ])) {
111
+ if ! isAlphaNumeric (rune (name [0 ])) {
106
112
return fmt .Errorf ("invalid class %q, should start with a letter or digit" , name )
107
113
}
108
114
if len (name ) == 1 {
109
115
return nil
110
116
}
111
117
for _ , c := range string (name [1 : len (name )- 1 ]) {
112
118
switch {
113
- case IsAlphaNumeric (c ):
119
+ case isAlphaNumeric (c ):
114
120
case c == '_' || c == '-' || c == '.' || c == ':' :
115
121
default :
116
122
return fmt .Errorf ("invalid character '%c' in device name %q" ,
117
123
c , name )
118
124
}
119
125
}
120
- if ! IsAlphaNumeric (rune (name [len (name )- 1 ])) {
126
+ if ! isAlphaNumeric (rune (name [len (name )- 1 ])) {
121
127
return fmt .Errorf ("invalid name %q, should end with a letter or digit" , name )
122
128
}
123
129
return nil
124
130
}
125
131
126
- // IsLetter reports whether the rune is a letter.
127
- func IsLetter (c rune ) bool {
132
+ // ParseFullyQualifiedName splits a fully-qualified name into device vendor, class,
133
+ // and name. If the device fails to parse as a qualified name, or if any
134
+ // of the split components fail to pass syntax validation, vendor and
135
+ // class are returned as empty, together with the verbatim input as the
136
+ // name and an error describing the reason for failure.
137
+ func ParseFullyQualifiedName (device string ) (string , string , string , error ) {
138
+ vendor , class , name := parseDevice (device )
139
+
140
+ if vendor == "" {
141
+ return "" , "" , device , fmt .Errorf ("unqualified device %q, missing vendor" , device )
142
+ }
143
+ if class == "" {
144
+ return "" , "" , device , fmt .Errorf ("unqualified device %q, missing class" , device )
145
+ }
146
+ if name == "" {
147
+ return "" , "" , device , fmt .Errorf ("unqualified device %q, missing device name" , device )
148
+ }
149
+
150
+ if err := ValidateVendorName (vendor ); err != nil {
151
+ return "" , "" , device , fmt .Errorf ("invalid device %q: %w" , device , err )
152
+ }
153
+ if err := ValidateClassName (class ); err != nil {
154
+ return "" , "" , device , fmt .Errorf ("invalid device %q: %w" , device , err )
155
+ }
156
+ if err := ValidateDeviceName (name ); err != nil {
157
+ return "" , "" , device , fmt .Errorf ("invalid device %q: %w" , device , err )
158
+ }
159
+
160
+ return vendor , class , name , nil
161
+ }
162
+
163
+ // parseDevice tries to split a device name into vendor, class, and name.
164
+ // If this fails, for instance in the case of unqualified device names,
165
+ // parseDevice returns an empty vendor and class together with name set
166
+ // to the verbatim input.
167
+ func parseDevice (device string ) (string , string , string ) {
168
+ if device == "" || device [0 ] == '/' {
169
+ return "" , "" , device
170
+ }
171
+
172
+ parts := strings .SplitN (device , "=" , 2 )
173
+ if len (parts ) != 2 || parts [0 ] == "" || parts [1 ] == "" {
174
+ return "" , "" , device
175
+ }
176
+
177
+ name := parts [1 ]
178
+ vendor , class := ParseKind (parts [0 ])
179
+ if vendor == "" {
180
+ return "" , "" , device
181
+ }
182
+
183
+ return vendor , class , name
184
+ }
185
+
186
+ // ParseKind splits a device qualifier into vendor and class.
187
+ // The syntax for a device qualifier is
188
+ //
189
+ // "<vendor>/<class>"
190
+ //
191
+ // If parsing fails, an empty vendor and the class set to the
192
+ // verbatim input is returned.
193
+ func ParseKind (kind string ) (string , string ) {
194
+ parts := strings .SplitN (kind , "/" , 2 )
195
+ if len (parts ) != 2 || parts [0 ] == "" || parts [1 ] == "" {
196
+ return "" , kind
197
+ }
198
+ return parts [0 ], parts [1 ]
199
+ }
200
+
201
+ // isLetter reports whether the rune is a letter.
202
+ func isLetter (c rune ) bool {
128
203
return ('A' <= c && c <= 'Z' ) || ('a' <= c && c <= 'z' )
129
204
}
130
205
131
206
// IsDigit reports whether the rune is a digit.
132
- func IsDigit (c rune ) bool {
207
+ func isDigit (c rune ) bool {
133
208
return '0' <= c && c <= '9'
134
209
}
135
210
136
- // IsAlphaNumeric reports whether the rune is a letter or digit.
137
- func IsAlphaNumeric (c rune ) bool {
138
- return IsLetter (c ) || IsDigit (c )
211
+ // isAlphaNumeric reports whether the rune is a letter or digit.
212
+ func isAlphaNumeric (c rune ) bool {
213
+ return isLetter (c ) || isDigit (c )
139
214
}
0 commit comments