Skip to content

Commit bc6f44c

Browse files
committed
fix parser logic to parse some patterns as strings
Signed-off-by: sivchari <shibuuuu5@gmail.com>
1 parent 9c43c07 commit bc6f44c

3 files changed

Lines changed: 36 additions & 16 deletions

File tree

pkg/crd/schema.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@ func mapToSchema(ctx *schemaContext, mapType *ast.MapType) *apiextensionsv1.JSON
424424
}
425425

426426
return &apiextensionsv1.JSONSchemaProps{
427+
//nolint:goconst // this is a constant, but it's more readable to have it here
427428
Type: "object",
428429
AdditionalProperties: &apiextensionsv1.JSONSchemaPropsOrBool{
429430
Schema: valSchema,

pkg/markers/parse.go

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ func guessType(scanner *sc.Scanner, raw string, allowSlice bool) *Argument {
317317
// might be a map or slice, parse the string and check for colon
318318
// (blech, basically arbitrary look-ahead due to raw strings).
319319
var keyVal string // just ignore this
320-
(&Argument{Type: StringType}).parseString(subScanner, raw, reflect.Indirect(reflect.ValueOf(&keyVal)))
320+
(&Argument{Type: StringType}).parseString(subScanner, raw, reflect.Indirect(reflect.ValueOf(&keyVal)), true)
321321

322322
if token := subScanner.Scan(); token == ':' || hint == '}' {
323323
// it's got a string followed by a colon -- it's a map
@@ -362,15 +362,20 @@ func guessType(scanner *sc.Scanner, raw string, allowSlice bool) *Argument {
362362
nextTok = subScanner.Scan()
363363
}
364364

365-
// Only treat as a numeric type if the scanner did not emit errors while
366-
// tokenising (e.g. invalid octal "087bdd" produces sc.Int but also
367-
// triggers an error). In that case fall through to the bare-string
368-
// path so the value is treated as a string.
369-
if !hadScanError {
370-
if nextTok == sc.Int {
371-
return &Argument{Type: IntType}
372-
}
373-
if nextTok == sc.Float {
365+
// Only treat as a numeric type if:
366+
// 1. The scanner did not emit errors while tokenising (e.g. invalid
367+
// octal "087bdd" produces sc.Int but also triggers an error).
368+
// 2. The token is immediately followed by a delimiter (comma,
369+
// semicolon, EOF) or whitespace-then-delimiter, meaning there is
370+
// no trailing non-numeric content. Inputs like "123/config",
371+
// "100Mi", "2024-01-15" start with a valid numeric token but
372+
// contain additional characters and must be treated as strings.
373+
if !hadScanError && (nextTok == sc.Int || nextTok == sc.Float) {
374+
followTok := peekNoSpace(subScanner)
375+
if followTok == ',' || followTok == ';' || followTok == sc.EOF || followTok == '}' {
376+
if nextTok == sc.Int {
377+
return &Argument{Type: IntType}
378+
}
374379
return &Argument{Type: NumberType}
375380
}
376381
}
@@ -381,7 +386,8 @@ func guessType(scanner *sc.Scanner, raw string, allowSlice bool) *Argument {
381386
}
382387

383388
// parseString parses either of the two accepted string forms (quoted, or bare tokens).
384-
func (a *Argument) parseString(scanner *sc.Scanner, raw string, out reflect.Value) {
389+
// When inMap is true, ':' is treated as a delimiter (map key separator).
390+
func (a *Argument) parseString(scanner *sc.Scanner, raw string, out reflect.Value, inMap bool) {
385391
// we need to temporarily disable the scanner's int/float parsing, since we want to
386392
// prevent number parsing errors.
387393
oldMode := scanner.Mode
@@ -406,10 +412,16 @@ func (a *Argument) parseString(scanner *sc.Scanner, raw string, out reflect.Valu
406412
}
407413

408414
// the "hard" case -- bare tokens not including ',' (the argument
409-
// separator), ';' (the slice separator), ':' (the map separator), or '}'
410-
// (delimitted slice ender)
415+
// separator), ';' (the slice separator), or '}' (delimitted slice ender).
416+
// ':' (the map separator) is only a delimiter when parsing inside a map.
417+
isDelim := func(hint rune) bool {
418+
if hint == ',' || hint == ';' || hint == '}' || hint == sc.EOF {
419+
return true
420+
}
421+
return inMap && hint == ':'
422+
}
411423
startPos := scanner.Position.Offset
412-
for hint := peekNoSpace(scanner); hint != ',' && hint != ';' && hint != ':' && hint != '}' && hint != sc.EOF; hint = peekNoSpace(scanner) {
424+
for hint := peekNoSpace(scanner); !isDelim(hint); hint = peekNoSpace(scanner) {
413425
// skip this token
414426
scanner.Scan()
415427
}
@@ -477,7 +489,7 @@ func (a *Argument) parseMap(scanner *sc.Scanner, raw string, out reflect.Value)
477489
}
478490

479491
for hint := peekNoSpace(scanner); hint != '}' && hint != sc.EOF; hint = peekNoSpace(scanner) {
480-
a.parseString(scanner, raw, key)
492+
a.parseString(scanner, raw, key, true)
481493
if !expect(scanner, ':', "colon") {
482494
return
483495
}
@@ -573,7 +585,7 @@ func (a *Argument) parse(scanner *sc.Scanner, raw string, out reflect.Value, inS
573585
// strings are a bit weird -- the "easy" case is quoted strings (tokenized as strings),
574586
// the "hard" case (present for backwards compat) is a bare sequence of tokens that aren't
575587
// a comma.
576-
a.parseString(scanner, raw, out)
588+
a.parseString(scanner, raw, out, false)
577589
case BoolType:
578590
if !expect(scanner, sc.Ident, "true or false") {
579591
return

pkg/markers/parse_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,13 @@ var _ = Describe("Parsing", func() {
222222

223223
It("should parse number-ish strings with invalid octal prefix as string", argParseTestCase{arg: anyArg, raw: `087bdd`, output: "087bdd"}.Run)
224224
It("should parse path-like strings as string", argParseTestCase{arg: anyArg, raw: `/tmp/tmp.4paHkdEcpK`, output: "/tmp/tmp.4paHkdEcpK"}.Run)
225+
It("should parse numeric path as string", argParseTestCase{arg: anyArg, raw: `123/config`, output: "123/config"}.Run)
226+
It("should parse invalid octal number as string", argParseTestCase{arg: anyArg, raw: `0987`, output: "0987"}.Run)
227+
It("should parse resource quantity as string", argParseTestCase{arg: anyArg, raw: `100Mi`, output: "100Mi"}.Run)
228+
It("should parse date-like string as string", argParseTestCase{arg: anyArg, raw: `2024-01-15`, output: "2024-01-15"}.Run)
229+
It("should parse SHA prefix as string", argParseTestCase{arg: anyArg, raw: `sha256:08a7b`, output: "sha256:08a7b"}.Run)
230+
It("should parse UUID-like string as string", argParseTestCase{arg: anyArg, raw: `00000000-1234-5678`, output: "00000000-1234-5678"}.Run)
231+
It("should parse alphanumeric starting with number as string", argParseTestCase{arg: anyArg, raw: `123abc`, output: "123abc"}.Run)
225232
})
226233
})
227234
})

0 commit comments

Comments
 (0)