@@ -16,6 +16,7 @@ package simpleschema
1616
1717import (
1818 "fmt"
19+ "regexp"
1920 "slices"
2021 "strconv"
2122 "strings"
@@ -30,6 +31,7 @@ const (
3031 keyTypeBoolean = string (AtomicTypeBool )
3132 keyTypeNumber = "number"
3233 keyTypeObject = "object"
34+ keyTypeArray = "array"
3335)
3436
3537// A predefinedType is a type that is predefined in the schema.
@@ -202,7 +204,7 @@ func (tf *transformer) handleSliceType(key, fieldType string) (*extv1.JSONSchema
202204 }
203205
204206 fieldJSONSchemaProps := & extv1.JSONSchemaProps {
205- Type : "array" ,
207+ Type : keyTypeArray ,
206208 Items : & extv1.JSONSchemaPropsOrArray {
207209 Schema : & extv1.JSONSchemaProps {},
208210 },
@@ -225,6 +227,7 @@ func (tf *transformer) handleSliceType(key, fieldType string) (*extv1.JSONSchema
225227 return fieldJSONSchemaProps , nil
226228}
227229
230+ //nolint:gocyclo
228231func (tf * transformer ) applyMarkers (schema * extv1.JSONSchemaProps , markers []* Marker , key string , parentSchema * extv1.JSONSchemaProps ) error {
229232 for _ , marker := range markers {
230233 switch marker .MarkerType {
@@ -265,7 +268,7 @@ func (tf *transformer) applyMarkers(schema *extv1.JSONSchemaProps, markers []*Ma
265268 }
266269 schema .Maximum = & val
267270 case MarkerTypeValidation :
268- if marker .Value == "" {
271+ if strings . TrimSpace ( marker .Value ) == "" {
269272 return fmt .Errorf ("validation failed" )
270273 }
271274 validation := []extv1.ValidationRule {
@@ -316,6 +319,75 @@ func (tf *transformer) applyMarkers(schema *extv1.JSONSchemaProps, markers []*Ma
316319 if len (enumJSONValues ) > 0 {
317320 schema .Enum = enumJSONValues
318321 }
322+ case MarkerTypeMinLength :
323+ // MinLength is only valid for string types
324+ if schema .Type != keyTypeString {
325+ return fmt .Errorf ("minLength marker is only valid for string types, got type: %s" , schema .Type )
326+ }
327+ val , err := strconv .ParseInt (marker .Value , 10 , 64 )
328+ if err != nil {
329+ return fmt .Errorf ("failed to parse minLength value: %w" , err )
330+ }
331+ schema .MinLength = & val
332+
333+ case MarkerTypeMaxLength :
334+ // MaxLength is only valid for string types
335+ if schema .Type != keyTypeString {
336+ return fmt .Errorf ("maxLength marker is only valid for string types, got type: %s" , schema .Type )
337+ }
338+ val , err := strconv .ParseInt (marker .Value , 10 , 64 )
339+ if err != nil {
340+ return fmt .Errorf ("failed to parse maxLength value: %w" , err )
341+ }
342+ schema .MaxLength = & val
343+ case MarkerTypePattern :
344+ if marker .Value == "" {
345+ return fmt .Errorf ("pattern marker value cannot be empty" )
346+ }
347+ // Pattern is only valid for string types
348+ if schema .Type != keyTypeString {
349+ return fmt .Errorf ("pattern marker is only valid for string types, got type: %s" , schema .Type )
350+ }
351+ // Validate regex
352+ if _ , err := regexp .Compile (marker .Value ); err != nil {
353+ return fmt .Errorf ("invalid pattern regex: %w" , err )
354+ }
355+ schema .Pattern = marker .Value
356+ case MarkerTypeUniqueItems :
357+ // UniqueItems is only valid for array types
358+ switch isUnique , err := strconv .ParseBool (marker .Value ); {
359+ case err != nil :
360+ return fmt .Errorf ("failed to parse uniqueItems marker value: %w" , err )
361+ case schema .Type != keyTypeArray :
362+ return fmt .Errorf ("uniqueItems marker is only valid for array types, got type: %s" , schema .Type )
363+ case isUnique :
364+ // Always set x-kubernetes-list-type to "set" when uniqueItems is true
365+ // https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions
366+ // https://stackoverflow.com/questions/79399232/forbidden-uniqueitems-cannot-be-set-to-true-since-the-runtime-complexity-become
367+ schema .XListType = ptr .To ("set" )
368+ default :
369+ // ignore
370+ }
371+ case MarkerTypeMinItems :
372+ // MinItems is only valid for array types
373+ if schema .Type != keyTypeArray {
374+ return fmt .Errorf ("minItems marker is only valid for array types, got type: %s" , schema .Type )
375+ }
376+ val , err := strconv .ParseInt (marker .Value , 10 , 64 )
377+ if err != nil {
378+ return fmt .Errorf ("failed to parse minItems value: %w" , err )
379+ }
380+ schema .MinItems = & val
381+ case MarkerTypeMaxItems :
382+ // MaxItems is only valid for array types
383+ if schema .Type != keyTypeArray {
384+ return fmt .Errorf ("maxItems marker is only valid for array types, got type: %s" , schema .Type )
385+ }
386+ val , err := strconv .ParseInt (marker .Value , 10 , 64 )
387+ if err != nil {
388+ return fmt .Errorf ("failed to parse maxItems value: %w" , err )
389+ }
390+ schema .MaxItems = & val
319391 }
320392 }
321393 return nil
0 commit comments