Skip to content

Commit 193c23c

Browse files
docs: update docs for v0.21 (#151)
* docs: updated docs to v0.21 * docs: update docs * Update docs/docs/core-concepts/3-parsing.mdx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * docs: preprocess and fixes --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
1 parent 2da0c0c commit 193c23c

11 files changed

+100
-121
lines changed

README.md

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func main() {
8282
u := User{}
8383
m := map[string]string{
8484
"name": "Zog",
85-
"age": "", // won't return an error because fields are optional by default
85+
"age": "", // won't return an error because fields are optional by default
8686
}
8787
errsMap := userSchema.Parse(m, &u)
8888
if errsMap != nil {
@@ -151,20 +151,15 @@ var t = time.Time
151151
errsList := Time().Required().Parse("2020-01-01T00:00:00Z", &t)
152152
```
153153

154-
#### **7 And do stuff before and after parsing**
154+
#### **7 Transform Data without limits**
155155

156156
```go
157157
var dest []string
158-
Slice(String().Email().Required()).PreTransform(func(data any, ctx z.Ctx) (any, error) {
159-
s := data.(string)
158+
schema := z.Preprocess(func(data any, ctx z.Ctx) ([]string, error) {
159+
s := data.(string) // don't do this, actually check the type
160160
return strings.Split(s, ","), nil
161-
}).PostTransform(func(destPtr any, ctx z.Ctx) error {
162-
s := destPtr.(*[]string)
163-
for i, v := range *s {
164-
(*s)[i] = strings.TrimSpace(v)
165-
}
166-
return nil
167-
161+
}, z.Slice(z.String().Trim().Email().Required()))
162+
168163
```
169164

170165
## Roadmap

docs/docs/changes-from-zod.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ toc_max_heading_level: 4
1212
2. I have chosen to make Zog prioritize idiomatic Golang over the Zod API. Meaning some of the schemas & tests (validation rules) have changed names, `z.Array()` is `z.Slice()`, `z.String().StartsWith()` is `z.String().HasPrefix` (to follow the std lib). Etc.
1313
3. When I felt like a Zod method name would be confusing for Golang devs I changed it
1414
- Some other changes:
15-
- The refine method for providing a custom validation function is renamed to `schema.Test()`
15+
- The refine & superRefine methods for providing a custom validation function is renamed to `schema.TestFunc` & `schema.Test()`
1616
- schemas are optional by default (in zod they are required)
1717
- The `z.Enum()` type from zod is removed in favor of `z.String().OneOf()` and is only supported for strings and numbers
1818
- `string().regex` is renamed to `z.String().Match()` as that is in line with the regexp methods from the standard library (i.e `regexp.Match` and `regexp.MatchString()`)

docs/docs/core-concepts/1-anatomy-of-schema.md

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,51 +7,28 @@ sidebar_position: 1
77
A zog schema is an interface implemented by multiple custom structs that represent a set of `validation` and `transformation` logic for a variable of a given type. For example:
88

99
```go
10-
stringSchema := z.String().Min(3).Required().Trim() // A zog schema that represents a required string of minimum 3 characters and will be trimmed for white space
10+
stringSchema := z.String().Trim().Min(3).Required() // A zog schema that represents a required string which will first be trimmed then a test to ensure it has 3+ characters will be ran.
1111
userSchema := z.Struct(z.Schema{"name": stringSchema}) // a zog schema that represents a user struct. Also yes I know that z.Schema might be confusing but think of it as the schema for the struct not a ZogSchema
1212
```
1313

1414
**The string schema, for example, looks something like this:**
1515

1616
```go
1717
type stringSchema struct {
18-
preTransforms []PreTransforms // transformations executed before the validation. For example trimming the string
1918
isRequired bool // optional. Defaults to FALSE
2019
defaultValue string // optional. if the input value is a "zero value" it will be replaced with this. Tests will still run on this value.
2120
catchValue string // optional. If this is set it will "catch" any errors, set the destination value to this value and exit
22-
tests []Test // These are your validation checks. Such as .Min(), .Contains(), etc
23-
postTransforms []PostTransform // transformations executed after the validation.
21+
testOrTransformation TestOrTransformation // This is the test or transformation that will be applied to the data.
2422
}
2523
```
2624

27-
## PreTransforms
28-
29-
Pretransforms is a list of function that are applied to the data before the [tests](#tests) are run. You can think of it like a `pipeline` of pre validation transformations for a specific schema. These are similar to preprocess functions in zod. **PreTransforms are PURE functions**. They take in data and return new data. This is the function signature:
30-
31-
```go
32-
// takes the data as input and returns the new data which will then be passed onto the next functions.
33-
// The function may return an error or a ZogIssue. In this case all validation will be skipped and the error will be wrapped into a ZogIssue and entire execution will return.
34-
type PreTransform = func(data any, ctx Ctx) (out any, err error)
35-
```
36-
37-
You can use pretransforms for things like trimming whitespace, splitting strings, etc. Here is an example of splitting a string into a slice of strings:
38-
39-
```go
40-
z.Slice(z.String()).PreTransform(func(data any, ctx Ctx) (any, error) {
41-
return strings.Split(data.(string), ","), nil
42-
}).Parse("item1,item2,item3", &dest)
43-
```
44-
45-
> **FOOTGUNS** > _Type Coercion_: Please note that pretransforms are executed before type coercion (if using `schema.Parse()`). This means that if you are responsible for checking that the data matches your expected type. If you blinding typecast the data to, for example, an int and your user provides a string as input you will cause a panic.
46-
> _Pure Functions_: Since pretransforms are pure functions. A copy of the data is passed in. So if you place a preTransform on a large struct it will copy the entire struct which is not efficient. Be careful!
47-
4825
## Required, Default and Catch
4926

5027
`schema.Required()` is a boolean that indicates if the field is required. If it is required and the data is a zero value the schema will return a [ZogIssue](/errors).
5128

5229
`schema.Default(value)` sets a default value for the field. If the data is a zero value it will be replaced with this value, this takes priority over required. Tests will still run on this value.
5330

54-
`schema.Catch(value)` sets a catch value. If this is set it will "catch" any errors or ZogIssues with the catch value. Meaning it will set the destination value to the catch value and exit. When this is triggered, no matter what error triggers it code will automatically jump to the [PostTransforms](#posttransforms). For more information checkout the [parsing execution structure](/core-concepts/parsing#parsing-execution-structure).
31+
`schema.Catch(value)` sets a catch value. If this is set it will "catch" any errors or ZogIssues with the catch value. Meaning it will set the destination value to the catch value and exit. When this is triggered, no matter what error triggers it code will automatically exit. For more information checkout the [parsing execution structure](/core-concepts/parsing#parsing-execution-structure).
5532

5633
## Tests
5734

@@ -71,28 +48,29 @@ z.String().Min(3, z.IssuePath("name")) // Thi
7148

7249
You are also free to create custom tests and pass them to the `schema.Test()` and `schema.TestFunc()` methods. For more details on this checkout the [Creating Custom Tests](/custom-tests) page.
7350

74-
## PostTransforms
51+
## Transforms
7552

76-
PostTransforms is a list of function that are applied to the data after the [tests](#tests) are run. You can think of it like a `pipeline` of transformations for a specific schema. This is the function signature:
53+
Transforms is a list of function that are applied to the data at any point. You can think of it like a `pipeline` of transformations for a specific schema. This is the function signature:
7754

7855
```go
79-
// type for functions called after validation & parsing is done
80-
type PostTransform = func(dataPtr any, ctx Ctx) error
56+
// Transforms are generic functions that take a pointer to the data as input. For primitive types you won't have to typecast but for complex types it will just be a any type and you will have to manually typecast it.
57+
type Transform[T any] func(dataPtr T, ctx Ctx) error
8158
```
8259

8360
As you can see the function takes a pointer to the data as input. This is to allow the function to modify the data.
8461

85-
You can use posttransforms for any transformation you want to do but don't want it to affect the validation process. For example imagine you want to validate a phone number and afterwards separate the string into the area code and the rest of the number.
86-
8762
```go
8863
type User struct {
8964
Phone string
9065
AreaCode string
9166
}
9267

9368
z.Struct(z.Schema{
94-
"phone": z.String().Test(...),
95-
}).PostTransform(func(dataPtr any, ctx z.Ctx) error {
69+
"phone": z.String().Test(...).Transform(func (valPtr *string, ctx z.Ctx) error{
70+
*valPtr = strings.ReplaceAll(*valPtr, " ", "") // remove all spaces
71+
return nil
72+
}),
73+
}).Transform(func(dataPtr any, ctx z.Ctx) error {
9674
user := dataPtr.(*User)
9775
user.AreaCode = user.Phone[:3]
9876
user.Phone = user.Phone[3:]

docs/docs/core-concepts/3-parsing.mdx

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -84,22 +84,14 @@ z.String(z.WithCoercer(func(data any) (any, error) {
8484

8585
## Parsing Execution Structure
8686

87-
![Zog Schema Parsign Execution Structure](./img/parsing-workflow.png)
87+
Parsing execution structure is quite simple. It just does roughly this:
88+
89+
1. Check for nil value
90+
- If nil take into account if schema has default value or is required
91+
2. Coerce value to the correct type otherwise
92+
3. Run validation loop
93+
- If a test fails, an issue is added
94+
- If you return an error from a transform, the execution stops and that error is returned.
95+
- If catch is active any error triggers it and stops the execution.
8896

89-
1. Pretransforms
90-
- On error all parsing and validation stops and [ZogIssues](/errors) are returned.
91-
- Can be caught by catch
92-
2. Default Check -> Assigns default value if the value is nil value
93-
3. Optional Check -> Stops validation if the value is nil value
94-
4. Casting -> Attempts to cast the value to the correct type
95-
- On error all parsing and validation stops and [ZogIssues](/errors) are returned
96-
- Can be caught by catch
97-
5. Required check ->
98-
- On error: aborts if the value is its nil value and returns a required [ZogIssue](/errors).
99-
- Can be caught by catch
100-
6. Tests -> Run all tests on the value (including required)
101-
- On error: validation [ZogIssues](/errors) are added to the [ZogIssueList](/errors#zogissuelist) or [ZogIssueMap](/errors#zogissuelist). All validation functions are run even if one of them fails.
102-
- Can be caught by catch
103-
7. PostTransforms -> Run all postTransforms on the value.
104-
- On error you return: aborts and adds your error to the list of [ZogIssues](/errors)
105-
- Only run on valid values. Won't run if a [ZogIssue](/errors) was created before the postTransforms
97+
![Zog Schema Parsign Execution Structure](./img/parsing-workflow.png)
Loading

docs/docs/core-concepts/parsing-table.tsx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -190,14 +190,14 @@ const schemaData = [
190190
schemaType: "Bool()",
191191
data: '""',
192192
dest: "false",
193-
requiredError: "yes",
193+
requiredError: "no",
194194
coercionError: "yes",
195195
},
196196
{
197197
schemaType: "Bool()",
198198
data: '" "',
199199
dest: "false",
200-
requiredError: "yes",
200+
requiredError: "no",
201201
coercionError: "yes",
202202
},
203203
{
@@ -260,14 +260,14 @@ const schemaData = [
260260
schemaType: "String()",
261261
data: '""',
262262
dest: '""',
263-
requiredError: "yes",
263+
requiredError: "no",
264264
coercionError: "no",
265265
},
266266
{
267267
schemaType: "String()",
268268
data: '" "',
269269
dest: '""',
270-
requiredError: "yes",
270+
requiredError: "no",
271271
coercionError: "no",
272272
},
273273
{
@@ -309,14 +309,14 @@ const schemaData = [
309309
schemaType: "Int()",
310310
data: '""',
311311
dest: 0,
312-
requiredError: "yes",
312+
requiredError: "no",
313313
coercionError: "yes",
314314
},
315315
{
316316
schemaType: "Int()",
317317
data: '" "',
318318
dest: 0,
319-
requiredError: "yes",
319+
requiredError: "no",
320320
coercionError: "yes",
321321
},
322322
{
@@ -372,14 +372,14 @@ const schemaData = [
372372
schemaType: "Float()",
373373
data: '""',
374374
dest: 0,
375-
requiredError: "yes",
375+
requiredError: "no",
376376
coercionError: "yes",
377377
},
378378
{
379379
schemaType: "Float()",
380380
data: '" "',
381381
dest: 0,
382-
requiredError: "yes",
382+
requiredError: "no",
383383
coercionError: "yes",
384384
},
385385
{
@@ -421,14 +421,14 @@ const schemaData = [
421421
schemaType: "Time()",
422422
data: '""',
423423
dest: "time.Time{}",
424-
requiredError: "yes",
424+
requiredError: "no",
425425
coercionError: "yes",
426426
},
427427
{
428428
schemaType: "Time()",
429429
data: '" "',
430430
dest: "time.Time{}",
431-
requiredError: "yes",
431+
requiredError: "no",
432432
coercionError: "yes",
433433
},
434434
{
@@ -470,14 +470,14 @@ const schemaData = [
470470
schemaType: "Slice()",
471471
data: '""',
472472
dest: '[""]',
473-
requiredError: "yes",
473+
requiredError: "no",
474474
coercionError: "no (error will show in the appropriate schema if any)",
475475
},
476476
{
477477
schemaType: "Slice()",
478478
data: '" "',
479479
dest: '[" "]',
480-
requiredError: "yes",
480+
requiredError: "no",
481481
coercionError: "no (error will show in the appropriate schema if any)",
482482
},
483483
{

docs/docs/core-design-decisions.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ toc_max_heading_level: 4
99

1010
- All fields optional by default. Same as graphql
1111
- When parsing into structs, private fields are ignored (same as stdlib json.Unmarshal)
12-
- The struct parser expects a `DataProvider` (although if you pass something else to the data field it will try to coerce it into a `DataProvider`), which is an interface that wraps around an input like a map. This is less efficient than doing it directly but allows us to reuse the same code for all kinds of data sources (i.e json, query params, forms, etc). Generally as a normal user you should ignore that `DataProviders` exist. So forget you ever read this.
13-
- Errors returned by you (for example in a `PreTransform` or `PostTransform` function) can be the ZogIssue interface or an error. If you return an error, it will be wrapped in a ZogIssue. ZogIssue is just a struct that wraps around an error and adds a message field which is text that can be shown to the user. For more on this see [Errors](/errors)
12+
- Errors returned by you (for example in a `Preprocess` or `Transform` function) can be the ZogIssue interface or an error. If you return an error, it will be wrapped in a ZogIssue. ZogIssue is just a struct that wraps around an error and adds a message field which is text that can be shown to the user. For more on this see [Errors](/errors)
1413
- You should not depend on test execution order. They might run in parallel in the future
1514

1615
> **A WORD OF CAUTION. ZOG & PANICS**
@@ -47,8 +46,6 @@ Most of these things are issues we would like to address in future versions.
4746
- Unsupported schemas:
4847

4948
- `z.Map()`
50-
- custom types -> i.e `type MyString string`
51-
- custom interfaces as types -> i.e the `Valuer` interface
5249

5350
- `zhttp` does not support parsing into any data type that is not a struct
5451
- Schema & pick, omit, etc are not really typesafe. i.e `z.Struct(z.Schema{"name"})` name is not typesafe

docs/docs/custom-tests.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
sidebar_position: 4
2+
sidebar_position: 5
33
toc_min_heading_level: 2
44
toc_max_heading_level: 4
55
---
@@ -13,8 +13,8 @@ toc_max_heading_level: 4
1313
All schemas contain the `TestFunc()` method which can be used to create a simple custom test in a similar way to Zod's `refine` method. The `TestFunc()` method takes a `ValidateFunc` as an argument. This is a function that takes the data as input and returns a boolean indicating if it is valid or not. If you return `false` from the function Zog will create a [ZogIssue](/errors). For example:
1414

1515
```go
16-
z.String().TestFunc(func(data any, ctx z.Ctx) bool {
17-
return data == "test"
16+
z.String().TestFunc(func(data *string, ctx z.Ctx) bool { // notice that here Zog already knows you need to pass a *string to the test.
17+
return *data == "test"
1818
})
1919
```
2020

@@ -23,7 +23,7 @@ Test funcs for structs and slices instead receive a pointer to the data to avoid
2323
```go
2424
z.Struct(z.Schema{
2525
"name": z.String(),
26-
}).TestFunc(func(dataPtr any, ctx z.Ctx) bool {
26+
}).TestFunc(func(dataPtr any, ctx z.Ctx) bool { // notice that here we have to cast the dataPtr because no inference for struct types
2727
user := dataPtr.(*User)
2828
return user.Name == "test"
2929
})
@@ -70,23 +70,24 @@ In general I recommend you wrap you reusable tests in a function. Here are examp
7070
```go
7171

7272
// Notice how we can pass default values to the test which can then be overriden by the function called. This is super nice if you need it!
73-
func MySimpleTest(opts ...z.TestOption) z.Test {
73+
func MySimpleTest(opts ...z.TestOption) z.Test[any] {
7474
options := []TestOption{
7575
Message("Default message, can be overriden"),
7676
}
7777
options = append(options, opts...)
7878
return z.TestFunc(
7979
func (val any, ctx z.Ctx) bool {
80-
return true // or any other validation
80+
user := val.(*User)
81+
return user.Name == "test" // or any other validation
8182
},
8283
...options
8384
)
8485
}
8586

8687

87-
func MyComplexTest() z.Test {
88+
func MyComplexTest() z.Test[*string] {
8889
return z.Test{
89-
Func: func (val any, ctx z.Ctx) {
90+
Func: func (valPtr *string, ctx z.Ctx) {
9091
// complex test here
9192
}
9293
}

docs/docs/getting-started.md

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -110,18 +110,13 @@ var t = time.Time
110110
errsList := Time().Required().Parse("2020-01-01T00:00:00Z", &t)
111111
```
112112

113-
#### **7 And do stuff before and after parsing**
113+
#### **7 Transform Data without limits**
114114

115115
```go
116116
var dest []string
117-
Slice(String().Email().Required()).PreTransform(func(data any, ctx z.Ctx) (any, error) {
118-
s := data.(string)
117+
schema := z.Preprocess(func(data any, ctx z.Ctx) ([]string, error) {
118+
s := data.(string) // don't do this, actually check the type
119119
return strings.Split(s, ","), nil
120-
}).PostTransform(func(destPtr any, ctx z.Ctx) error {
121-
s := destPtr.(*[]string)
122-
for i, v := range *s {
123-
(*s)[i] = strings.TrimSpace(v)
124-
}
125-
return nil
126-
120+
}, z.Slice(z.String().Trim().Email().Required()))
121+
127122
```

0 commit comments

Comments
 (0)