redefiniendo el builder

packs2
DEV 3 years ago
parent 858f700161
commit 7e8cfee304

@ -33,7 +33,7 @@ type (
MiTipo struct {
FS1 string `json:"nombre,omitempty,binary"`
FU2 uint
FU2 uint `json:"-"`
FB3 bool
}
@ -56,15 +56,15 @@ func reflects_main () {
rf := reflect.TypeOf(t)
rs := reflects.RStruct{}
for i:=0; i<rf.NumField(); i++ {
f := rf.Field(i)
fmt.Printf("Campo %s %s\n",f.Name,f.Tag.Get("json"))
reflects.NewRFieldTag(f,"json",func(rf reflect.Type,pos int, name string) uint8 {
fmt.Printf("Opcion %s\n",name)
return 1
})
rs.Fields = append(rs.Fields,*reflects.NewRField(rf.Field(i)))
}
for i,f := range rs.Fields {
fmt.Printf("%d tags %v\n",i,f.Tags)
}
}

@ -27,23 +27,26 @@ import (
"activething.com/go/gogo/core/opts"
"activething.com/go/gogo/core/slices"
"reflect"
"strings"
"unicode"
)
type (
RField struct {
RType reflect.Type
RField reflect.StructField
Name string
SName string // scaped name
HName string // html name
EqualFoldFn func(s, t []byte) bool
BName []byte // name bytes
Index []int
Tags map[string]slices.String
EqualFoldFn func(s, t []byte) bool
}
)
@ -53,18 +56,56 @@ type (
func NewRField (rfield reflect.StructField, options ...opts.OptionFnc[RField]) *RField {
rf := &RField {
RField: rfield,
Tags : make(map[string]slices.String),
}
rf.parseTag("json",nil)
return opts.Apply(rf,options...)
}
func (f *RField) parseTag (tag string) slices.String {
var l slices.String
s,h := f.Tags[tag]
if !h {
func (f *RField) parseTag (tagKey string, onOptionFn func(*RField,int,string)) {
if _,h := f.Tags[tagKey]; !h {
t := f.RField.Tag.Get(tagKey)
if len(t) > 0 {
l := strings.Split(t,",")
f.Tags[tagKey]= l
if onOptionFn != nil {
for i,o := range l {
onOptionFn(f,i,o)
}
}
}
}
}
func parseTag (field reflect.StructField, tagKey string, onOptionFn func(int,string)) (slices.String,bool) {
t := field.Tag.Get(tagKey)
if len(t) > 0 {
l := strings.Split(t,",")
if onOptionFn != nil {
for i,o := range l {
onOptionFn(i,o)
}
}
return l,true
}
return nil,true
}
func isValidTag(s string) bool {
if s == "" {
return false
}
for _, c := range s {
switch {
case strings.ContainsRune("!#$%&()*+-./:;<=>?@[]^_{|}~ ", c):
case !unicode.IsLetter(c) && !unicode.IsDigit(c):
return false
}
}
return l
return true
}

@ -23,7 +23,11 @@
package reflects
import (
"activething.com/go/gogo/core/slices"
"bytes"
"reflect"
)
type (
@ -35,224 +39,119 @@ type (
func NewRStruct (rtype reflect.Type) *RStruct {
res := &RStruct{
Fields : make(RFields,0,rtype.NumField()),
NameIndex: make(map[string]int),
}
cur := RFields{}
nxt := RFields{{ RType: rtype }}
/*
func NewRStruct (t reflect.Type) *RStruct {
// Anonymous Fields to explore at the current level and the next.
current := RFields{}
next := []RField{{RType: t}}
// Count of queued names for current level and the next.
var count, nextCount map[reflect.Type]int
// Types already visited at an earlier level.
visited := map[reflect.Type]bool{}
// Fields found.
var fs []RField
// Buffer to run HTMLEscape on RField names.
var nameEscBuf bytes.Buffer
var (
cnt,nxc map[reflect.Type]int
vis map[reflect.Type]struct{}
fls RFields
nms bytes.Buffer
)
for len(next) > 0 {
current, next = next, current[:0]
count, nextCount = nextCount, map[reflect.Type]int{}
for len(nxt) > 0 {
cur, nxt = nxt, cur[:0]
cnt, nxc = nxc, map[reflect.Type]int{}
for _, f := range current {
if visited[f.RType] {
for _,f := range cur {
if _,h := vis[f.RType]; h {
continue
}
visited[f.RType] = true
vis[f.RType]=struct{}{}
// Scan f.RType for Fields to include.
for i :=0; i<f.RType.NumField(); i++ {
var flags RFieldFlags
sf := f.RType.Field(i)
if sf.Anonymous {
t := sf.Type
if t.Kind() == reflect.Pointer {
t = t.Elem()
st := sf.Type
if st.Kind() == reflect.Pointer {
st = st.Elem()
}
if !sf.IsExported() && t.Kind() != reflect.Struct {
// Ignore embedded Fields of unexported non-struct types.
if !sf.IsExported() && st.Kind() != reflect.Struct {
continue
}
// Do not ignore embedded Fields of unexported struct types
// since they may have exported Fields.
} else if !sf.IsExported() {
// Ignore unexported non-embedded Fields.
continue
}
tag := sf.Tag.Get("json")
if tag == "-" {
nm := false
tags,ok := parseTag(sf,"json",nil)
if ok {
if tags.Has("-") {
continue
}
name, opts := parseTag(tag)
if !isValidTag(name) {
name = ""
if isValidTag(tags[0]) {
nm=true
}
}
index := make([]int, len(f.Index)+1)
copy(index, f.Index)
index[len(f.Index)] = i
ft := sf.Type
if ft.Name() == "" && ft.Kind() == reflect.Pointer {
// Follow pointer.
ft=ft.Elem()
}
// Only strings, floats, integers, and booleans can be quoted.
if opts.Contains("string") {
qt := false
if tags.Has("string") {
switch ft.Kind() {
case reflect.Bool,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
reflect.Float32, reflect.Float64,
reflect.String:
flags|=RFieldQuoted
}
qt = true
}
// Record found RField and index sequence.
if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
if name != "" {
flags |= RFieldFlagTagged
}
if name == "" {
name = sf.Name
}
rf := RField{
Name : name,
Index: index,
RType: ft,
}
if opts.Contains("omitempty") {
rf.Flags|=RFieldOmitEmpty
}
index := make([]int, len(f.Index)+1)
copy(index, f.Index)
index[len(f.Index)] = i
rf.BName = []byte(rf.Name)
//rf.EqualFold = foldFunc(rf.BName)
// Build nameEscHTML and nameNonEsc ahead of time.
nameEscBuf.Reset()
nameEscBuf.WriteString(`"`)
//HTMLEscape(&nameEscBuf, rf.BName)
nameEscBuf.WriteString(`":`)
rf.HName = nameEscBuf.String()
//rf. = `"` + rf.Name + `":`
fs = append(fs, rf)
if count[f.RType] > 1 {
// If there were multiple instances, add a second,
// so that the annihilation code will see a duplicate.
// It only cares about the distinction between 1 or 2,
// so don't bother generating any more copies.
fs = append(fs, fs[len(fs)-1])
}
continue
}
// Record new anonymous struct to explore in next round.
nextCount[ft]++
if nextCount[ft] == 1 {
next = append(next, RField{Name: ft.Name(), Index: index, RType: ft})
}
}
}
var name string
if nm || sf.Anonymous || ft.Kind() != reflect.Struct {
if nm {
name = tags[0]
} else {
name = sf.Name
}
sort.Slice(fs, func(i, j int) bool {
x := fs
// sort RField by name, breaking ties with depth, then
// breaking ties with "name came from json tag", then
// breaking ties with index sequence.
if x[i].Name != x[j].Name {
return x[i].Name < x[j].Name
}
if len(x[i].Index) != len(x[j].Index) {
return len(x[i].Index) < len(x[j].Index)
}
if x[i].Tagged() != x[j].Tagged() {
return x[i].Tagged()
rf := RField{
RField: sf,
RType: ft,
Name: name,
BName: []byte(name),
Tags: map[string]slices.String{"json": append(slices.String(nil), tags...)},
}
return RFieldsByIndex(x).Less(i, j)
})
// Delete all Fields that are hidden by the Go rules for embedded Fields,
// except that Fields with JSON tags are promoted.
// The Fields are sorted in primary order of name, secondary order
// of RField index length. Loop over names; for each name, delete
// hidden Fields by choosing the one dominant RField that survives.
out := fs[:0]
for advance, i := 0, 0; i < len(fs); i += advance {
// One iteration per name.
// Find the sequence of Fields with the name of this first RField.
fi := fs[i]
name := fi.Name
for advance = 1; i+advance < len(fs); advance++ {
fj := fs[i+advance]
if fj.Name != name {
break
}
res.Fields = append(res.Fields, rf)
if cnt[f.RType] > 1 {
res.Fields = append(res.Fields, res.Fields[len(res.Fields)-1])
}
if advance == 1 { // Only one RField with this name
out = append(out, fi)
continue
}
dominant, ok := RFields(fs[i : i+advance]).Dominant()
if ok {
out = append(out, dominant)
}
}
fs = out
sort.Sort(RFieldsByIndex(fs))
for i := range fs {
//f := &fs[i]
//f.encoder = typeEncoder(typeByIndex(t, f.index))
nxc[ft]++
if nxc[ft] == 1 {
nxt = append(nxt,RField{
RType: ft,
Name: ft.Name(),
Index: index,
})
}
nameIndex := make(map[string]int, len(fs))
for i, f := range fs {
nameIndex[f.Name] = i
}
return &RStruct{
Fields: fs,
FieldsIndex: nameIndex,
}
}
return res
func parseTag(tag string) (string, tagOptions) {
tag, opt, _ := strings.Cut(tag, ",")
return tag, tagOptions(opt)
}
func isValidTag(s string) bool {
if s == "" {
return false
}
for _, c := range s {
switch {
case strings.ContainsRune("!#$%&()*+-./:;<=>?@[]^_{|}~ ", c):
// Backslash and quote chars are reserved, but
// otherwise any punctuation chars are allowed
// in a tag name.
case !unicode.IsLetter(c) && !unicode.IsDigit(c):
return false
}
}
return true
}
*/
Loading…
Cancel
Save

Powered by TurnKey Linux.