Skip to content

Commit 5a5d2a5

Browse files
committed
Use producer validation from parser package
Signed-off-by: Evan Lezar <[email protected]>
1 parent c26e138 commit 5a5d2a5

File tree

2 files changed

+96
-97
lines changed

2 files changed

+96
-97
lines changed

api/producer/identifiers.go

Lines changed: 87 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ import (
2121
"strings"
2222
)
2323

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+
2430
// ValidateKind checks the validity of a CDI kind.
2531
// The syntax for a device kind“ is
2632
//
@@ -74,19 +80,19 @@ func validateVendorOrClassName(name string) error {
7480
if name == "" {
7581
return fmt.Errorf("empty name")
7682
}
77-
if !IsLetter(rune(name[0])) {
83+
if !isLetter(rune(name[0])) {
7884
return fmt.Errorf("%q, should start with letter", name)
7985
}
8086
for _, c := range string(name[1 : len(name)-1]) {
8187
switch {
82-
case IsAlphaNumeric(c):
88+
case isAlphaNumeric(c):
8389
case c == '_' || c == '-' || c == '.':
8490
default:
8591
return fmt.Errorf("invalid character '%c' in name %q",
8692
c, name)
8793
}
8894
}
89-
if !IsAlphaNumeric(rune(name[len(name)-1])) {
95+
if !isAlphaNumeric(rune(name[len(name)-1])) {
9096
return fmt.Errorf("%q, should end with a letter or digit", name)
9197
}
9298

@@ -102,38 +108,107 @@ func ValidateDeviceName(name string) error {
102108
if name == "" {
103109
return fmt.Errorf("invalid (empty) device name")
104110
}
105-
if !IsAlphaNumeric(rune(name[0])) {
111+
if !isAlphaNumeric(rune(name[0])) {
106112
return fmt.Errorf("invalid class %q, should start with a letter or digit", name)
107113
}
108114
if len(name) == 1 {
109115
return nil
110116
}
111117
for _, c := range string(name[1 : len(name)-1]) {
112118
switch {
113-
case IsAlphaNumeric(c):
119+
case isAlphaNumeric(c):
114120
case c == '_' || c == '-' || c == '.' || c == ':':
115121
default:
116122
return fmt.Errorf("invalid character '%c' in device name %q",
117123
c, name)
118124
}
119125
}
120-
if !IsAlphaNumeric(rune(name[len(name)-1])) {
126+
if !isAlphaNumeric(rune(name[len(name)-1])) {
121127
return fmt.Errorf("invalid name %q, should end with a letter or digit", name)
122128
}
123129
return nil
124130
}
125131

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 {
128203
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')
129204
}
130205

131206
// IsDigit reports whether the rune is a digit.
132-
func IsDigit(c rune) bool {
207+
func isDigit(c rune) bool {
133208
return '0' <= c && c <= '9'
134209
}
135210

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)
139214
}

pkg/parser/parser.go

Lines changed: 9 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@
1717
package parser
1818

1919
import (
20-
"fmt"
2120
"strings"
21+
22+
"tags.cncf.io/container-device-interface/api/producer"
2223
)
2324

2425
// QualifiedName returns the qualified name for a device.
@@ -39,7 +40,7 @@ func QualifiedName(vendor, class, name string) string {
3940

4041
// IsQualifiedName tests if a device name is qualified.
4142
func IsQualifiedName(device string) bool {
42-
_, _, _, err := ParseQualifiedName(device)
43+
err := producer.ValidateQualifiedName(device)
4344
return err == nil
4445
}
4546

@@ -49,35 +50,15 @@ func IsQualifiedName(device string) bool {
4950
// class are returned as empty, together with the verbatim input as the
5051
// name and an error describing the reason for failure.
5152
func ParseQualifiedName(device string) (string, string, string, error) {
52-
vendor, class, name := ParseDevice(device)
53-
54-
if vendor == "" {
55-
return "", "", device, fmt.Errorf("unqualified device %q, missing vendor", device)
56-
}
57-
if class == "" {
58-
return "", "", device, fmt.Errorf("unqualified device %q, missing class", device)
59-
}
60-
if name == "" {
61-
return "", "", device, fmt.Errorf("unqualified device %q, missing device name", device)
62-
}
63-
64-
if err := ValidateVendorName(vendor); err != nil {
65-
return "", "", device, fmt.Errorf("invalid device %q: %w", device, err)
66-
}
67-
if err := ValidateClassName(class); err != nil {
68-
return "", "", device, fmt.Errorf("invalid device %q: %w", device, err)
69-
}
70-
if err := ValidateDeviceName(name); err != nil {
71-
return "", "", device, fmt.Errorf("invalid device %q: %w", device, err)
72-
}
73-
74-
return vendor, class, name, nil
53+
return producer.ParseFullyQualifiedName(device)
7554
}
7655

7756
// ParseDevice tries to split a device name into vendor, class, and name.
7857
// If this fails, for instance in the case of unqualified device names,
7958
// ParseDevice returns an empty vendor and class together with name set
8059
// to the verbatim input.
60+
//
61+
// Deprecated: This function will be removed. Use producer.ParseQualifiedName instead.
8162
func ParseDevice(device string) (string, string, string) {
8263
if device == "" || device[0] == '/' {
8364
return "", "", device
@@ -118,11 +99,7 @@ func ParseQualifier(kind string) (string, string) {
11899
// - digits ('0'-'9')
119100
// - underscore, dash, and dot ('_', '-', and '.')
120101
func ValidateVendorName(vendor string) error {
121-
err := validateVendorOrClassName(vendor)
122-
if err != nil {
123-
err = fmt.Errorf("invalid vendor. %w", err)
124-
}
125-
return err
102+
return producer.ValidateVendorName(vendor)
126103
}
127104

128105
// ValidateClassName checks the validity of class name.
@@ -131,39 +108,7 @@ func ValidateVendorName(vendor string) error {
131108
// - digits ('0'-'9')
132109
// - underscore, dash, and dot ('_', '-', and '.')
133110
func ValidateClassName(class string) error {
134-
err := validateVendorOrClassName(class)
135-
if err != nil {
136-
err = fmt.Errorf("invalid class. %w", err)
137-
}
138-
return err
139-
}
140-
141-
// validateVendorOrClassName checks the validity of vendor or class name.
142-
// A name may contain the following ASCII characters:
143-
// - upper- and lowercase letters ('A'-'Z', 'a'-'z')
144-
// - digits ('0'-'9')
145-
// - underscore, dash, and dot ('_', '-', and '.')
146-
func validateVendorOrClassName(name string) error {
147-
if name == "" {
148-
return fmt.Errorf("empty name")
149-
}
150-
if !IsLetter(rune(name[0])) {
151-
return fmt.Errorf("%q, should start with letter", name)
152-
}
153-
for _, c := range string(name[1 : len(name)-1]) {
154-
switch {
155-
case IsAlphaNumeric(c):
156-
case c == '_' || c == '-' || c == '.':
157-
default:
158-
return fmt.Errorf("invalid character '%c' in name %q",
159-
c, name)
160-
}
161-
}
162-
if !IsAlphaNumeric(rune(name[len(name)-1])) {
163-
return fmt.Errorf("%q, should end with a letter or digit", name)
164-
}
165-
166-
return nil
111+
return producer.ValidateClassName(class)
167112
}
168113

169114
// ValidateDeviceName checks the validity of a device name.
@@ -172,28 +117,7 @@ func validateVendorOrClassName(name string) error {
172117
// - digits ('0'-'9')
173118
// - underscore, dash, dot, colon ('_', '-', '.', ':')
174119
func ValidateDeviceName(name string) error {
175-
if name == "" {
176-
return fmt.Errorf("invalid (empty) device name")
177-
}
178-
if !IsAlphaNumeric(rune(name[0])) {
179-
return fmt.Errorf("invalid class %q, should start with a letter or digit", name)
180-
}
181-
if len(name) == 1 {
182-
return nil
183-
}
184-
for _, c := range string(name[1 : len(name)-1]) {
185-
switch {
186-
case IsAlphaNumeric(c):
187-
case c == '_' || c == '-' || c == '.' || c == ':':
188-
default:
189-
return fmt.Errorf("invalid character '%c' in device name %q",
190-
c, name)
191-
}
192-
}
193-
if !IsAlphaNumeric(rune(name[len(name)-1])) {
194-
return fmt.Errorf("invalid name %q, should end with a letter or digit", name)
195-
}
196-
return nil
120+
return producer.ValidateDeviceName(name)
197121
}
198122

199123
// IsLetter reports whether the rune is a letter.

0 commit comments

Comments
 (0)