Skip to content

Commit 924b7cd

Browse files
authored
Fix string length validation inconsistency between zod and validator (#19)
1 parent 62e6d4a commit 924b7cd

2 files changed

Lines changed: 22 additions & 22 deletions

File tree

zod.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -947,27 +947,27 @@ func (c *Converter) validateString(validate string) string {
947947
// const FishEnum = z.enum(["Salmon", "Tuna", "Trout"]);
948948
validateStr.WriteString(fmt.Sprintf(".enum([\"%s\"] as const)", strings.Join(vals, "\", \"")))
949949
case "len":
950-
validateStr.WriteString(fmt.Sprintf(".length(%s)", valValue))
950+
refines = append(refines, fmt.Sprintf(".refine((val) => [...val].length === %s, 'String must contain %s character(s)')", valValue, valValue))
951951
case "min":
952-
validateStr.WriteString(fmt.Sprintf(".min(%s)", valValue))
952+
refines = append(refines, fmt.Sprintf(".refine((val) => [...val].length >= %s, 'String must contain at least %s character(s)')", valValue, valValue))
953953
case "max":
954-
validateStr.WriteString(fmt.Sprintf(".max(%s)", valValue))
954+
refines = append(refines, fmt.Sprintf(".refine((val) => [...val].length <= %s, 'String must contain at most %s character(s)')", valValue, valValue))
955955
case "gt":
956956
val, err := strconv.Atoi(valValue)
957957
if err != nil {
958958
panic("gt= must be followed by a number")
959959
}
960-
validateStr.WriteString(fmt.Sprintf(".min(%d)", val+1))
960+
refines = append(refines, fmt.Sprintf(".refine((val) => [...val].length > %d, 'String must contain at least %d character(s)')", val, val+1))
961961
case "gte":
962-
validateStr.WriteString(fmt.Sprintf(".min(%s)", valValue))
962+
refines = append(refines, fmt.Sprintf(".refine((val) => [...val].length >= %s, 'String must contain at least %s character(s)')", valValue, valValue))
963963
case "lt":
964964
val, err := strconv.Atoi(valValue)
965965
if err != nil {
966966
panic("lt= must be followed by a number")
967967
}
968-
validateStr.WriteString(fmt.Sprintf(".max(%d)", val-1))
968+
refines = append(refines, fmt.Sprintf(".refine((val) => [...val].length < %d, 'String must contain at most %d character(s)')", val, val-1))
969969
case "lte":
970-
validateStr.WriteString(fmt.Sprintf(".max(%s)", valValue))
970+
refines = append(refines, fmt.Sprintf(".refine((val) => [...val].length <= %s, 'String must contain at most %s character(s)')", valValue, valValue))
971971
case "contains":
972972
validateStr.WriteString(fmt.Sprintf(".includes(\"%s\")", valValue))
973973
case "endswith":

zod_test.go

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -394,10 +394,10 @@ func TestNullableWithValidations(t *testing.T) {
394394
PtrInt2: z.number().gte(2).lte(5),
395395
PtrIntNullable: z.number().gte(2).lte(5).nullable(),
396396
PtrStringOptional1: z.string().optional(),
397-
PtrStringOptional2: z.string().min(2).max(5).optional(),
398-
PtrString1: z.string().min(2).max(5),
399-
PtrString2: z.string().min(2).max(5),
400-
PtrStringNullable: z.string().min(2).max(5).nullable(),
397+
PtrStringOptional2: z.string().refine((val) => [...val].length >= 2, 'String must contain at least 2 character(s)').refine((val) => [...val].length <= 5, 'String must contain at most 5 character(s)').optional(),
398+
PtrString1: z.string().refine((val) => [...val].length >= 2, 'String must contain at least 2 character(s)').refine((val) => [...val].length <= 5, 'String must contain at most 5 character(s)'),
399+
PtrString2: z.string().refine((val) => [...val].length >= 2, 'String must contain at least 2 character(s)').refine((val) => [...val].length <= 5, 'String must contain at most 5 character(s)'),
400+
PtrStringNullable: z.string().refine((val) => [...val].length >= 2, 'String must contain at least 2 character(s)').refine((val) => [...val].length <= 5, 'String must contain at most 5 character(s)').nullable(),
401401
})
402402
export type User = z.infer<typeof UserSchema>
403403
@@ -487,7 +487,7 @@ export type OneOfSeparated = z.infer<typeof OneOfSeparatedSchema>
487487
}
488488
assert.Equal(t,
489489
`export const LenSchema = z.object({
490-
Name: z.string().length(5),
490+
Name: z.string().refine((val) => [...val].length === 5, 'String must contain 5 character(s)'),
491491
})
492492
export type Len = z.infer<typeof LenSchema>
493493
@@ -499,7 +499,7 @@ export type Len = z.infer<typeof LenSchema>
499499
}
500500
assert.Equal(t,
501501
`export const MinSchema = z.object({
502-
Name: z.string().min(5),
502+
Name: z.string().refine((val) => [...val].length >= 5, 'String must contain at least 5 character(s)'),
503503
})
504504
export type Min = z.infer<typeof MinSchema>
505505
@@ -511,7 +511,7 @@ export type Min = z.infer<typeof MinSchema>
511511
}
512512
assert.Equal(t,
513513
`export const MaxSchema = z.object({
514-
Name: z.string().max(5),
514+
Name: z.string().refine((val) => [...val].length <= 5, 'String must contain at most 5 character(s)'),
515515
})
516516
export type Max = z.infer<typeof MaxSchema>
517517
@@ -523,7 +523,7 @@ export type Max = z.infer<typeof MaxSchema>
523523
}
524524
assert.Equal(t,
525525
`export const MinMaxSchema = z.object({
526-
Name: z.string().min(3).max(7),
526+
Name: z.string().refine((val) => [...val].length >= 3, 'String must contain at least 3 character(s)').refine((val) => [...val].length <= 7, 'String must contain at most 7 character(s)'),
527527
})
528528
export type MinMax = z.infer<typeof MinMaxSchema>
529529
@@ -535,7 +535,7 @@ export type MinMax = z.infer<typeof MinMaxSchema>
535535
}
536536
assert.Equal(t,
537537
`export const GtSchema = z.object({
538-
Name: z.string().min(6),
538+
Name: z.string().refine((val) => [...val].length > 5, 'String must contain at least 6 character(s)'),
539539
})
540540
export type Gt = z.infer<typeof GtSchema>
541541
@@ -547,7 +547,7 @@ export type Gt = z.infer<typeof GtSchema>
547547
}
548548
assert.Equal(t,
549549
`export const GteSchema = z.object({
550-
Name: z.string().min(5),
550+
Name: z.string().refine((val) => [...val].length >= 5, 'String must contain at least 5 character(s)'),
551551
})
552552
export type Gte = z.infer<typeof GteSchema>
553553
@@ -559,7 +559,7 @@ export type Gte = z.infer<typeof GteSchema>
559559
}
560560
assert.Equal(t,
561561
`export const LtSchema = z.object({
562-
Name: z.string().max(4),
562+
Name: z.string().refine((val) => [...val].length < 5, 'String must contain at most 4 character(s)'),
563563
})
564564
export type Lt = z.infer<typeof LtSchema>
565565
@@ -571,7 +571,7 @@ export type Lt = z.infer<typeof LtSchema>
571571
}
572572
assert.Equal(t,
573573
`export const LteSchema = z.object({
574-
Name: z.string().max(5),
574+
Name: z.string().refine((val) => [...val].length <= 5, 'String must contain at most 5 character(s)'),
575575
})
576576
export type Lte = z.infer<typeof LteSchema>
577577
@@ -1456,7 +1456,7 @@ export type Lte = z.infer<typeof LteSchema>
14561456
}
14571457
assert.Equal(t,
14581458
`export const Dive1Schema = z.object({
1459-
Map: z.record(z.string(), z.string().min(2)).nullable(),
1459+
Map: z.record(z.string(), z.string().refine((val) => [...val].length >= 2, 'String must contain at least 2 character(s)')).nullable(),
14601460
})
14611461
export type Dive1 = z.infer<typeof Dive1Schema>
14621462
@@ -1467,7 +1467,7 @@ export type Dive1 = z.infer<typeof Dive1Schema>
14671467
}
14681468
assert.Equal(t,
14691469
`export const Dive2Schema = z.object({
1470-
Map: z.record(z.string(), z.string().min(3)).refine((val) => Object.keys(val).length >= 2, 'Map too small').array(),
1470+
Map: z.record(z.string(), z.string().refine((val) => [...val].length >= 3, 'String must contain at least 3 character(s)')).refine((val) => Object.keys(val).length >= 2, 'Map too small').array(),
14711471
})
14721472
export type Dive2 = z.infer<typeof Dive2Schema>
14731473
@@ -1478,7 +1478,7 @@ export type Dive2 = z.infer<typeof Dive2Schema>
14781478
}
14791479
assert.Equal(t,
14801480
`export const Dive3Schema = z.object({
1481-
Map: z.record(z.string().min(3), z.string().max(4)).refine((val) => Object.keys(val).length >= 2, 'Map too small').array(),
1481+
Map: z.record(z.string().refine((val) => [...val].length >= 3, 'String must contain at least 3 character(s)'), z.string().refine((val) => [...val].length <= 4, 'String must contain at most 4 character(s)')).refine((val) => Object.keys(val).length >= 2, 'Map too small').array(),
14821482
})
14831483
export type Dive3 = z.infer<typeof Dive3Schema>
14841484

0 commit comments

Comments
 (0)