/
builtins_validation.go
367 lines (339 loc) · 9.15 KB
/
builtins_validation.go
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
//go:build !b_no_validate
// +build !b_no_validate
package evaldo
import (
"fmt"
"net/mail"
"strconv"
"strings"
"time"
"github.com/refaktor/rye/env"
"github.com/refaktor/rye/util"
)
// Integer represents an integer.
type ValidationError struct {
message string
}
func Validation_EvalBlock(es *env.ProgramState, vals env.Dict) (env.Dict, map[string]env.Object) {
notes := make(map[string]env.Object, 0) // TODO ... what is this 2 here ... just for temp
var name string
var val any
res := make(map[string]any)
for es.Ser.Pos() < es.Ser.Len() {
object := es.Ser.Pop()
var verr env.Object
switch obj := object.(type) {
case env.Setword:
if name != "" {
// sets the previous value
res[name] = env.ToRyeValue(val)
}
name = es.Idx.GetWord(obj.Index)
res[name] = env.ToRyeValue(vals.Data[name])
case env.Word:
if name != "" {
val, verr = evalWord(obj, es, res[name])
if verr != nil {
notes[name] = verr
} else {
res[name] = val
}
}
default:
fmt.Println("Type is not matching - Validation_EvalBlock.")
//TODO-FIXME
}
}
//set the last value too
res[name] = env.ToRyeValue(val)
return *env.NewDict(res), notes
}
func Validation_EvalBlock_List(es *env.ProgramState, vals env.List) (env.Object, []env.Object) {
notes := make([]env.Object, 0) // TODO ... what is this 2 here ... just for temp
var res env.Object
for es.Ser.Pos() < es.Ser.Len() {
object := es.Ser.Pop()
var verr env.Object
switch obj := object.(type) {
case env.Word:
res, verr = evalWord_List(obj, es, vals)
if verr != nil {
notes = append(notes, verr)
}
default:
fmt.Println("Type is not matching - Validation_EvalBlock_List.")
//TODO-FIXME
}
}
//set the last value too
return res, notes
}
func newVE(n string) *ValidationError {
return &ValidationError{n}
}
func evalWord(word env.Word, es *env.ProgramState, val any) (any, env.Object) {
// later get all word indexes in adwance and store them only once... then use integer comparison in switch below
// this is two times BAD ... first it needs to retrieve a string of index (BIG BAD) and then it compares string to string
// instead of just comparing two integers
switch es.Idx.GetWord(word.Index) {
case "optional":
def := es.Ser.Pop()
if val == nil {
return def, nil
} else {
return val, nil
}
case "check":
serr := es.Ser.Pop()
switch blk := es.Ser.Pop().(type) {
case env.Block:
ser := es.Ser
es.Ser = blk.Series
EvalBlockInj(es, val.(env.Object), true)
es.Ser = ser
if es.Res.(env.Integer).Value > 0 {
return val, nil
} else {
return val, serr
}
default:
return val, nil // TODO ... make error
}
case "calc":
switch blk := es.Ser.Pop().(type) {
case env.Block:
ser := es.Ser
es.Ser = blk.Series
EvalBlockInj(es, val.(env.Object), true)
es.Ser = ser
return es.Res, nil
default:
return val, nil // TODO ... make error
}
case "required":
if val == nil {
return val, *env.NewString("required")
} else {
return val, nil
}
case "integer":
return evalInteger(val)
case "decimal":
return evalDecimal(val)
case "string":
return evalString(val)
case "email":
return evalEmail(val)
case "date":
return evalDate(val)
default:
return val, nil
}
}
func evalWord_List(word env.Word, es *env.ProgramState, vals env.List) (env.List, env.Object) {
// later get all word indexes in advance and store them only once... then use integer comparison in switch below
// this is two times BAD ... first it needs to retrieve a string of index (BIG BAD) and then it compares string to string
// instead of just comparing two integers
res := make([]any, 0)
switch es.Idx.GetWord(word.Index) {
case "some":
switch blk := es.Ser.Pop().(type) {
case env.Block:
for _, v := range vals.Data {
rit := BuiValidate(es, env.ToRyeValue(v), blk)
res = append(res, rit)
}
return *env.NewList(res), nil
default:
return *env.NewList(res), nil // TODO ... make error
}
default:
return vals, *env.NewString("unknown word in list validation") // TODO --- this is not a validation error exactly, but more like error in validation code .. think about
}
}
func evalInteger(val any) (any, env.Object) {
switch val1 := val.(type) {
case int64:
return *env.NewInteger(val1), nil
case env.Integer:
return val1, nil
case string:
v, e := strconv.Atoi(val1)
if e != nil {
return val, *env.NewString("not integer")
} else {
return *env.NewInteger(int64(v)), nil
}
case env.String:
v, e := strconv.Atoi(val1.Value)
if e != nil {
return val, *env.NewString("not integer")
} else {
return *env.NewInteger(int64(v)), nil
}
default:
return val, *env.NewString("not integer")
}
}
func evalDecimal(val any) (any, env.Object) {
switch val1 := val.(type) {
case float64:
return *env.NewDecimal(val1), nil
case env.Decimal:
return val1, nil
case string:
v, e := strconv.ParseFloat(val1, 64)
if e != nil {
return val, *env.NewString("not decimal")
} else {
return *env.NewDecimal(v), nil
}
case env.String:
v, e := strconv.ParseFloat(val1.Value, 64)
if e != nil {
return val, *env.NewString("not decimal")
} else {
return *env.NewDecimal(v), nil
}
default:
return val, *env.NewString("not decimal")
}
}
func evalString(val any) (any, env.Object) {
switch val1 := val.(type) {
case int64:
return *env.NewString(strconv.FormatInt(val1, 10)), nil
case env.Integer:
return *env.NewString(strconv.FormatInt(val1.Value, 10)), nil
case string:
return *env.NewString(val1), nil
case env.String:
return val1, nil
default:
return val1, *env.NewString("not string")
}
}
func parseEmail(v string) (any, env.Object) {
e, err := mail.ParseAddress(v)
if err != nil {
return v, *env.NewString("not email")
}
return *env.NewString(e.Address), nil
}
func evalEmail(val any) (any, env.Object) {
switch val1 := val.(type) {
case env.String:
return parseEmail(val1.Value)
case string:
return parseEmail(val1)
default:
return val, *env.NewString("not email")
}
}
func parseDate(v string) (any, env.Object) {
if strings.Index(v[0:3], ".") > 0 {
d, e := time.Parse("02.01.2006", v)
if e != nil {
return v, *env.NewString("not date")
}
fmt.Println(d)
return *env.NewDate(d), nil
} else if strings.Index(v[3:5], ":") > 0 {
d, e := time.Parse("2006-01-02", v)
if e != nil {
return v, *env.NewString("not date")
}
return *env.NewDate(d), nil
}
return v, *env.NewString("not date")
}
func evalDate(val any) (any, env.Object) {
switch val1 := val.(type) {
case env.String:
return parseDate(val1.Value)
case string:
return parseDate(val1)
default:
return val, *env.NewString("not date")
}
}
func BuiValidate(env1 *env.ProgramState, arg0 env.Object, arg1 env.Object) env.Object {
switch blk := arg1.(type) {
case env.Block:
switch rmap := arg0.(type) {
case env.Dict:
ser1 := env1.Ser
env1.Ser = blk.Series
val, verrs := Validation_EvalBlock(env1, rmap)
env1.Ser = ser1
if len(verrs) > 0 {
env1.FailureFlag = true
return env.NewError4(403, "validation error", nil, verrs)
}
return val
case env.List:
ser1 := env1.Ser
env1.Ser = blk.Series
val, _ := Validation_EvalBlock_List(env1, rmap)
env1.Ser = ser1
return val
default:
return *env.NewError("arg 1 should be Dict or List")
}
default:
return *env.NewError("arg 2 should be block")
}
}
func something() {
fmt.Print("1")
}
var Builtins_validation = map[string]*env.Builtin{
"validate": {
Argsn: 2,
Doc: "Validates Dictionary using the Validation dialect and returns result or a Failure.",
Fn: func(env1 *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
return BuiValidate(env1, arg0, arg1)
},
},
"validate>ctx": {
Argsn: 2,
Doc: "Validates Dictionary using the Validation dialect and returns result as a Context or a Failure.",
Fn: func(env1 *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
obj := BuiValidate(env1, arg0, arg1)
switch obj1 := obj.(type) {
case env.Dict:
return util.Dict2Context(env1, obj1)
default:
return obj1
}
},
},
/* "collect": {
Argsn: 1,
Fn: func(env1 *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
arg0.Trace("OPEN :::::::::")
switch str := arg0.(type) {
case env.Uri:
fmt.Println(str.Path)
db, _ := sql.Open("sqlite3", "temp-database") // TODO -- we need to make path parser in URI then this will be path
return *env.NewNative(env1.Idx, db, "Rye-sqlite")
default:
return env.NewError("arg 2 should be Uri")
}
},
},
"pulldown": {
Argsn: 2,
Fn: func(env1 *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
arg0.Trace("OPEN :::::::::")
switch str := arg0.(type) {
case env.Uri:
fmt.Println(str.Path)
db, _ := sql.Open("sqlite3", "temp-database") // TODO -- we need to make path parser in URI then this will be path
return *env.NewNative(env1.Idx, db, "Rye-sqlite")
default:
return env.NewError("arg 2 should be Uri")
}
},
},*/
}