Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 13 additions & 16 deletions boolean.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,6 @@ func (v *BoolSchema) setCoercer(c conf.CoercerFunc) {
v.coercer = c
}

// Internal function to process the data
func (v *BoolSchema) process(val any, dest any, path p.PathBuilder, ctx ParseCtx) {
primitiveProcessor(val, dest, path, ctx, v.preTransforms, v.tests, v.postTransforms, v.defaultVal, v.required, v.catch, v.coercer, p.IsParseZeroValue)
}

// ! USER FACING FUNCTIONS

// Returns a new Bool Schema
Expand All @@ -49,35 +44,37 @@ func Bool(opts ...SchemaOption) *BoolSchema {
}

// Parse data into destination pointer
func (v *BoolSchema) Parse(data any, dest *bool, options ...ParsingOption) p.ZogErrList {
func (v *BoolSchema) Parse(data any, dest *bool, options ...ExecOption) p.ZogErrList {
errs := p.NewErrsList()
ctx := p.NewParseCtx(errs, conf.ErrorFormatter)
ctx := p.NewExecCtx(errs, conf.ErrorFormatter)
for _, opt := range options {
opt(ctx)
}
path := p.PathBuilder("")

v.process(data, dest, path, ctx)
v.process(ctx.NewSchemaCtx(data, dest, p.PathBuilder(""), v.getType()))

return errs.List
}

// Internal function to process the data
func (v *BoolSchema) process(ctx *p.SchemaCtx) {
primitiveProcessor(ctx, v.preTransforms, v.tests, v.postTransforms, v.defaultVal, v.required, v.catch, v.coercer, p.IsParseZeroValue)
}

// Validate data against schema
func (v *BoolSchema) Validate(val *bool, options ...ParsingOption) p.ZogErrList {
func (v *BoolSchema) Validate(val *bool, options ...ExecOption) p.ZogErrList {
errs := p.NewErrsList()
ctx := p.NewParseCtx(errs, conf.ErrorFormatter)
ctx := p.NewExecCtx(errs, conf.ErrorFormatter)
for _, opt := range options {
opt(ctx)
}
path := p.PathBuilder("")

v.validate(val, path, ctx)
v.validate(ctx.NewSchemaCtx(val, val, p.PathBuilder(""), v.getType()))
return errs.List
}

// Internal function to validate data
func (v *BoolSchema) validate(val any, path p.PathBuilder, ctx ParseCtx) {
primitiveValidator(val, path, ctx, v.preTransforms, v.tests, v.postTransforms, v.defaultVal, v.required, v.catch)
func (v *BoolSchema) validate(ctx *p.SchemaCtx) {
primitiveValidator(ctx, v.preTransforms, v.tests, v.postTransforms, v.defaultVal, v.required, v.catch)
}

// GLOBAL METHODS
Expand Down
10 changes: 7 additions & 3 deletions boolean_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,14 @@ func TestBoolSchemaOption(t *testing.T) {
assert.Equal(t, true, result)
}

func TestParsingOption(t *testing.T) {
func TestExecOption(t *testing.T) {
t.Run("Parse context is passed to parsing option", func(t *testing.T) {
boolProc := Bool()
var result bool
var contextPassed bool

// Create a fake parsing option that checks if it receives a ParseCtx
fakeOption := func(p *p.ZogParseCtx) {
fakeOption := func(p *p.ExecCtx) {
if p != nil {
contextPassed = true
}
Expand Down Expand Up @@ -112,7 +112,7 @@ func TestBoolRequired(t *testing.T) {
},
}

boolProc := Bool().Required()
boolProc := Bool().Required(Message("test"))

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
Expand All @@ -123,6 +123,10 @@ func TestBoolRequired(t *testing.T) {
t.Errorf("On Run %s -> Expected error: %v, got: %v", test.name, test.expectErr, errs)
}

if test.expectErr && errs[0].Message() != "test" {
t.Errorf("On Run %s -> Expected error: %v, got: %v", test.name, "test", errs[0].Message())
}

if !test.expectErr && result != test.expected {
t.Errorf("On Run %s -> Expected %v, but got %v", test.name, test.expected, result)
}
Expand Down
4 changes: 2 additions & 2 deletions boolean_validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ func TestBoolValidate(t *testing.T) {
}
}

func TestBoolValidateParsingOption(t *testing.T) {
func TestBoolValidateExecOption(t *testing.T) {
t.Run("Parse context is passed to parsing option", func(t *testing.T) {
boolProc := Bool()
var result bool
var contextPassed bool

// Create a fake parsing option that checks if it receives a ParseCtx
fakeOption := func(p *p.ZogParseCtx) {
fakeOption := func(p *p.ExecCtx) {
if p != nil {
contextPassed = true
}
Expand Down
2 changes: 1 addition & 1 deletion conf/Errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
var DefaultErrMsgMap zconst.LangMap = en.Map

func NewDefaultFormatter(m zconst.LangMap) p.ErrFmtFunc {
return func(e p.ZogError, p p.ParseCtx) {
return func(e p.ZogError, p p.Ctx) {
if e.Message() != "" {
return
}
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ err := z.String().Min(5, z.MessageFunc(func(e z.ZogError, p z.Ctx) {
// err = []ZogError{{Message: "string must be at least 5 characters long"}}
```

#### **3. Using the WithErrFormatter() ParsingOption**
#### **3. Using the WithErrFormatter() ExecOption**

This allows you to set a custom error formatter for the entire parsing operation. Beware you must handle all error codes & types or you may get unexpected messages.

Expand Down
2 changes: 1 addition & 1 deletion i18n/i18n.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func SetLanguagesErrsMap(m map[string]zconst.LangMap, defaultLang string, opts .
op(&langKey)
}

conf.ErrorFormatter = func(e internals.ZogError, ctx internals.ParseCtx) {
conf.ErrorFormatter = func(e internals.ZogError, ctx internals.Ctx) {
lang := ctx.Get(langKey)
if lang != nil {
langM, ok := m[lang.(string)]
Expand Down
17 changes: 15 additions & 2 deletions i18n/i18n_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func TestSetLanguagesErrsMap(t *testing.T) {
// Define a schema for testing
schema := zog.Struct(zog.Schema{
"name": zog.String().Required(),
})
}).Required()

// Test cases
testCases := []struct {
Expand Down Expand Up @@ -76,16 +76,29 @@ func TestSetLanguagesErrsMap(t *testing.T) {
var dest struct {
Name string
}
dest2 := struct {
Name string
Age int
}{
Age: 1,
Name: tc.input["name"].(string),
}
errs := schema.Parse(tc.input, &dest, zog.WithCtxValue(LangKey, tc.lang))
errs2 := schema.Validate(&dest2, zog.WithCtxValue(LangKey, tc.lang))

if tc.expectedErr {
assert.NotNil(t, errs, "Expected errors, got nil")
assert.NotNil(t, errs2, "Expected errors, got nil")

nameErrs, ok := errs["name"]
assert.True(t, ok, "Expected errors for 'name' field")
assert.NotEmpty(t, nameErrs, "Expected at least one error for 'name' field")

assert.Equal(t, tc.expected, nameErrs[0].Message(), "Unexpected error message")

nameErrs2, ok2 := errs2["name"]
assert.True(t, ok2, "Expected errors for 'name' field")
assert.NotEmpty(t, nameErrs2, "Expected at least one error for 'name' field")
assert.Equal(t, tc.expected, nameErrs2[0].Message(), "Unexpected error message")
} else {
assert.Nil(t, errs, "Expected no errors, got: %v", errs)
}
Expand Down
175 changes: 175 additions & 0 deletions internals/contexts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package internals

import zconst "github.com/Oudwins/zog/zconst"

// Zog Context interface. This is the interface that is passed to schema tests, pre and post transforms
type Ctx interface {
// Get a value from the context
Get(key string) any
// Deprecated: Use Ctx.AddIssue() instead
// Please don't depend on this interface it may change
NewError(p PathBuilder, e ZogError)
// Adds an issue to the schema execution.
AddIssue(e ZogError)
// Please don't depend on this interface it may change
HasErrored() bool
}

func NewExecCtx(errs ZogErrors, fmter ErrFmtFunc) *ExecCtx {
return &ExecCtx{
Fmter: fmter,
Errors: errs,
}
}

type ExecCtx struct {
Fmter ErrFmtFunc
Errors ZogErrors
m map[string]any
}

func (c *ExecCtx) HasErrored() bool {
return !c.Errors.IsEmpty()
}

func (c *ExecCtx) SetErrFormatter(fmter ErrFmtFunc) {
c.Fmter = fmter
}

func (c *ExecCtx) Set(key string, val any) {
if c.m == nil {
c.m = make(map[string]any)
}
c.m[key] = val
}

func (c *ExecCtx) Get(key string) any {
return c.m[key]
}

// Adds a ZogError to the execution context.
func (c *ExecCtx) AddIssue(e ZogError) {
if e.Message() == "" {
c.Fmter(e, c)
}
c.Errors.Add(e.Path(), e)
}

// Deprecated: Use Ctx.AddIssue() instead
// This is old interface. It will be removed soon
func (c *ExecCtx) NewError(path PathBuilder, e ZogError) {
c.Errors.Add(path.String(), e)
}

// Internal. Used to format errors
func (c *ExecCtx) FmtErr(e ZogError) {
if e.Message() != "" {
return
}
c.Fmter(e, c)
}

func (c *ExecCtx) NewSchemaCtx(val any, destPtr any, path PathBuilder, dtype zconst.ZogType) *SchemaCtx {
return &SchemaCtx{
ExecCtx: c,
Val: val,
DestPtr: destPtr,
Path: path,
DType: dtype,
}
}

func (c *ExecCtx) NewValidateSchemaCtx(valPtr any, path PathBuilder, dtype zconst.ZogType) *SchemaCtx {
return &SchemaCtx{
ExecCtx: c,
Val: valPtr,
Path: path,
DType: dtype,
}
}

type SchemaCtx struct {
*ExecCtx
Val any
DestPtr any
Path PathBuilder
DType zconst.ZogType
}
type TestCtx struct {
*SchemaCtx
Test *Test
}

func (c *SchemaCtx) Issue() ZogError {
// TODO handle catch here
return &ZogErr{
EPath: c.Path.String(),
Typ: c.DType,
Val: c.Val,
}
}

// Please don't depend on this method it may change
func (c *SchemaCtx) IssueFromTest(test *Test, val any) ZogError {
e := &ZogErr{
EPath: c.Path.String(),
Typ: c.DType,
Val: val,
C: test.ErrCode,
ParamsM: test.Params,
}
if test.ErrFmt != nil {
test.ErrFmt(e, c)
}
return e
}

// Please don't depend on this method it may change
func (c *SchemaCtx) IssueFromCoerce(err error) ZogError {
return &ZogErr{
C: zconst.ErrCodeCoerce,
EPath: c.Path.String(),
Typ: c.DType,
Val: c.Val,
Err: err,
}
}

// Please don't depend on this method it may change
// Wraps an error in a ZogError if it is not already a ZogError
func (c *SchemaCtx) IssueFromUnknownError(err error) ZogError {
zerr, ok := err.(ZogError)
if !ok {
return c.Issue().SetError(err)
}
return zerr
}

func (c *TestCtx) Issue() ZogError {
// TODO handle catch here
return &ZogErr{
EPath: c.Path.String(),
Typ: c.DType,
Val: c.Val,
C: c.Test.ErrCode,
ParamsM: c.Test.Params,
}
}

func (c *TestCtx) FmtErr(e ZogError) {
if e.Message() != "" {
return
}

if c.Test.ErrFmt != nil {
c.Test.ErrFmt(e, c)
return
}

c.SchemaCtx.FmtErr(e)
}

func (c *TestCtx) AddIssue(e ZogError) {
c.FmtErr(e)
c.Errors.Add(c.Path.String(), e)
}
Loading
Loading