新增: u-tpl SQL 模板引擎完整实现
- Lexer/Parser/Executor 三阶段架构
- #{param} 参数化 + ${raw} 原样替换 + 白名单安全策略
- @if/@for/@tpl/@include/@namespace 控制流
- 表达式引擎: 比较、逻辑、nil 检查、len() 内置函数
- 支持 ?/$1/:1 多数据库占位符风格
- 零依赖,纯 Go 标准库实现
This commit is contained in:
68
internal/context.go
Normal file
68
internal/context.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
vars map[string]any
|
||||
}
|
||||
|
||||
func NewContext(vars map[string]any) *Context {
|
||||
return &Context{vars: vars}
|
||||
}
|
||||
|
||||
func (c *Context) Get(path string) (any, bool) {
|
||||
if c.vars == nil {
|
||||
return nil, false
|
||||
}
|
||||
return resolvePath(c.vars, path)
|
||||
}
|
||||
|
||||
func resolvePath(current any, path string) (any, bool) {
|
||||
for seg := range strings.SplitSeq(path, ".") {
|
||||
if current == nil {
|
||||
return nil, false
|
||||
}
|
||||
switch v := current.(type) {
|
||||
case map[string]any:
|
||||
var ok bool
|
||||
current, ok = v[seg]
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
default:
|
||||
val := reflect.ValueOf(current)
|
||||
for val.Kind() == reflect.Pointer {
|
||||
val = val.Elem()
|
||||
}
|
||||
if val.Kind() != reflect.Struct {
|
||||
return nil, false
|
||||
}
|
||||
field := val.FieldByName(seg)
|
||||
if !field.IsValid() {
|
||||
field = findFieldIgnoreCase(val, seg)
|
||||
}
|
||||
if !field.IsValid() {
|
||||
return nil, false
|
||||
}
|
||||
current = field.Interface()
|
||||
}
|
||||
}
|
||||
return current, true
|
||||
}
|
||||
|
||||
func findFieldIgnoreCase(v reflect.Value, name string) reflect.Value {
|
||||
typ := v.Type()
|
||||
for i := range typ.NumField() {
|
||||
f := typ.Field(i)
|
||||
if !f.IsExported() {
|
||||
continue
|
||||
}
|
||||
if strings.EqualFold(f.Name, name) {
|
||||
return v.Field(i)
|
||||
}
|
||||
}
|
||||
return reflect.Value{}
|
||||
}
|
||||
Reference in New Issue
Block a user