-
Notifications
You must be signed in to change notification settings - Fork 40
Expand file tree
/
Copy pathboxedSchema.go
More file actions
143 lines (127 loc) · 3.8 KB
/
Copy pathboxedSchema.go
File metadata and controls
143 lines (127 loc) · 3.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package zog
import (
"github.com/Oudwins/zog/conf"
p "github.com/Oudwins/zog/pkgs/internals"
"github.com/Oudwins/zog/zconst"
)
type CreateBoxFunc[T any, B any] func(data T, ctx Ctx) (B, error)
type UnboxFunc[B any, T any] func(data B, ctx Ctx) (T, error)
var _ ComplexZogSchema = &BoxedSchema[any, any]{}
type BoxedSchema[B any, T any] struct {
schema ZogSchema
unbox UnboxFunc[B, T]
box CreateBoxFunc[T, B]
}
func Boxed[B any, T any](schema ZogSchema, unboxFunc UnboxFunc[B, T], boxFunc CreateBoxFunc[T, B]) *BoxedSchema[B, T] {
return &BoxedSchema[B, T]{schema: schema, unbox: unboxFunc, box: boxFunc}
}
func (s *BoxedSchema[B, T]) Parse(data any, dest any, options ...ExecOption) ZogIssueList {
errs := p.NewErrsList()
defer errs.Free()
ctx := p.NewExecCtx(errs, conf.IssueFormatter)
defer ctx.Free()
for _, opt := range options {
opt(ctx)
}
path := p.NewPathBuilder()
defer path.Free()
sctx := ctx.NewSchemaCtx(data, dest, path, s.getType())
defer sctx.Free()
s.process(sctx)
return errs.List
}
func (s *BoxedSchema[B, T]) Validate(dest *B, options ...ExecOption) ZogIssueList {
errs := p.NewErrsList()
defer errs.Free()
ctx := p.NewExecCtx(errs, conf.IssueFormatter)
defer ctx.Free()
for _, opt := range options {
opt(ctx)
}
path := p.NewPathBuilder()
defer path.Free()
sctx := ctx.NewSchemaCtx(*dest, dest, path, s.getType())
defer sctx.Free()
s.validate(sctx)
return errs.List
}
func (s *BoxedSchema[B, T]) validate(ctx *p.SchemaCtx) {
boxPtr, ok := ctx.ValPtr.(*B)
if !ok {
// We have to go directly to the exec context as that is what formats. We cannot use ctx because it will try to catch the issue and this is an uncatchable issue
// since we cannot set the value as its not of type *B
ctx.ExecCtx.AddIssue(ctx.IssueFromInvalidType("pointer matching boxed schema type", ctx.ValPtr, "validating a boxed schema"))
return
}
unboxed, err := s.unbox(*boxPtr, ctx)
if err != nil {
ctx.AddIssue(ctx.IssueFromUnknownError(err))
return
}
ctx.Data = &unboxed
ctx.ValPtr = &unboxed
s.schema.validate(ctx)
// Re-box and propagate back
if s.box != nil {
newBox, err := s.box(unboxed, ctx)
if err != nil {
ctx.AddIssue(ctx.IssueFromUnknownError(err))
return
}
*boxPtr = newBox
}
}
func (s *BoxedSchema[B, T]) process(ctx *p.SchemaCtx) {
boxPtr, ok := ctx.ValPtr.(*B)
if !ok {
// We have to go directly to the exec context as that is what formats. We cannot use ctx because it will try to catch the issue and this is an uncatchable issue
// since we cannot set the value as its not of type *B
ctx.ExecCtx.AddIssue(ctx.IssueFromInvalidType("pointer matching boxed schema type", ctx.ValPtr, "processing a boxed schema"))
return
}
// 1. Handle ctx.Data - could be B, *B, or raw data
var innerData any
switch d := ctx.Data.(type) {
case B:
// Unbox B to get T
unboxed, err := s.unbox(d, ctx)
if err != nil {
ctx.AddIssue(ctx.IssueFromUnknownError(err))
return
}
innerData = unboxed
case *B:
// Dereference and unbox
unboxed, err := s.unbox(*d, ctx)
if err != nil {
ctx.AddIssue(ctx.IssueFromUnknownError(err))
return
}
innerData = unboxed
default:
// Raw data - pass directly to inner schema
innerData = d
}
// 2. Create temporary T for inner schema and pass data
var inner T
ctx.Data = innerData
ctx.ValPtr = &inner
// 3. Process through inner schema (keeps pointer to inner)
s.schema.process(ctx)
// 4. Re-box and set to original destination
if s.box != nil {
newBox, err := s.box(inner, ctx)
if err != nil {
ctx.AddIssue(ctx.IssueFromUnknownError(err))
return
}
*boxPtr = newBox
}
// TODO maybe some kind of flag that you executed process with boxFunc is nil
}
func (s *BoxedSchema[B, T]) getType() zconst.ZogType {
return s.schema.getType()
}
func (s *BoxedSchema[B, T]) setCoercer(c CoercerFunc) {
s.schema.setCoercer(c)
}