You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
165 lines
4.0 KiB
165 lines
4.0 KiB
// ------------------------------------------------------------------------
|
|
// 🚀 Project active3
|
|
// 🔗 Active Thing (activething.com) git.activething.com/go
|
|
//
|
|
// File name key_stringer.go
|
|
// ✍️ Created by DEV
|
|
// 📅 Modified 11/05/2024
|
|
//
|
|
// 🔒 Copyright 2024 activething.com
|
|
//
|
|
// 📜 LICENSE --------------------------------------------------------------
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
// ------------------------------------------------------------------------
|
|
|
|
package pkjson
|
|
|
|
import (
|
|
"apack"
|
|
"cdcs/cjson"
|
|
"errs"
|
|
"reflect"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type (
|
|
|
|
|
|
keyStringer struct {
|
|
sync.RWMutex
|
|
stringers map[reflect.Type]rMapEntryFnc
|
|
ifaceCheckers []func(p reflect.Type) rMapEntryFnc
|
|
}
|
|
|
|
keyStringerFnc func (s *keyStringer)
|
|
|
|
)
|
|
|
|
func newKeyStringer (options...keyStringerFnc) *keyStringer {
|
|
s := &keyStringer{
|
|
stringers: make(map[reflect.Type]rMapEntryFnc),
|
|
}
|
|
for _,o := range options {
|
|
o(s)
|
|
}
|
|
return s
|
|
}
|
|
|
|
|
|
|
|
func (s *keyStringer) packStringifyKey (b *Builder, key any) {
|
|
switch v := key.(type) {
|
|
case string : b.packString(v,apack.FlagScapeHTML)//
|
|
case bool : cjson.WriteBool (b.B,v)
|
|
case uint : cjson.WriteUint (b.B,uint64(v))
|
|
case uint8 : cjson.WriteUint (b.B,uint64(v))
|
|
case uint16 : cjson.WriteUint (b.B,uint64(v))
|
|
case uint32 : cjson.WriteUint (b.B,uint64(v))
|
|
case uintptr : cjson.WriteUint (b.B,uint64(v))
|
|
case uint64 : cjson.WriteUint (b.B,v)
|
|
case int : cjson.WriteInt (b.B,int64(v))
|
|
case int8 : cjson.WriteInt (b.B,int64(v))
|
|
case int16 : cjson.WriteInt (b.B,int64(v))
|
|
case int32 : cjson.WriteInt (b.B,int64(v))
|
|
case int64 : cjson.WriteInt (b.B,v)
|
|
case float64 : cjson.WriteFloat (b.B,v,64)
|
|
case float32 : cjson.WriteFloat (b.B,float64(v),32)
|
|
case time.Time : cjson.WriteTime (b.B,v,b.f.DateFormat())
|
|
case time.Duration : cjson.WriteInt (b.B,int64(v))
|
|
case error :
|
|
if ec,is := v.(errs.ErrCoder); is {
|
|
b.packString(ec.Code(),apack.FlagScapeHTML)
|
|
return
|
|
}
|
|
b.packString(v.Error(),apack.FlagScapeHTML)
|
|
|
|
default:
|
|
|
|
me := &rMapEntry{ v: reflect.ValueOf(key) }
|
|
fn := g.keyStringer.getOrBuildStringer(me.v.Type())
|
|
if fn == nil {
|
|
b.SetError(apack.G.ErrInvalidKey.WithMessage("cant convert Key to string"))
|
|
return
|
|
}
|
|
if er := fn(me,me.v); er != nil {
|
|
b.SetError(er)
|
|
return
|
|
}
|
|
b.packString(me.k,me.f)
|
|
}
|
|
}
|
|
|
|
|
|
func (s *keyStringer) getOrBuildStringer(rtype reflect.Type) rMapEntryFnc {
|
|
s.RLock()
|
|
if fn,hs := s.stringers[rtype]; hs {
|
|
s.Unlock()
|
|
return fn
|
|
}
|
|
|
|
fn := s.checkInterfaces(rtype)
|
|
if fn == nil {
|
|
fn = s.defaultStringer
|
|
}
|
|
s.RUnlock()
|
|
|
|
if fn != nil {
|
|
s.Lock()
|
|
s.stringers[rtype]=fn
|
|
s.Unlock()
|
|
}
|
|
|
|
return fn
|
|
}
|
|
|
|
|
|
|
|
func (s *keyStringer) defaultStringer(e *rMapEntry, k reflect.Value) error {
|
|
k = k.Elem()
|
|
|
|
fn := s.stringers[k.Type()]
|
|
if fn != nil {
|
|
return fn(e,k)
|
|
}
|
|
if fn = s.checkInterfaces(k.Type()); fn != nil {
|
|
return fn(e,k)
|
|
}
|
|
|
|
return apack.G.ErrInvalidKey.WithMessageF("%s", k.String())
|
|
}
|
|
|
|
|
|
|
|
func (s *keyStringer) RegKeyStringer (rtype reflect.Type, stringerFn rMapEntryFnc) {
|
|
if stringerFn == nil {
|
|
return
|
|
}
|
|
s.Lock()
|
|
s.stringers[rtype]=stringerFn
|
|
s.Unlock()
|
|
}
|
|
|
|
func (s *keyStringer) RegInterfaceChecker (checkfn ... func(p reflect.Type) rMapEntryFnc) {
|
|
s.ifaceCheckers = append(s.ifaceCheckers,checkfn ...) }
|
|
|
|
|
|
func (s *keyStringer) checkInterfaces (rtype reflect.Type) rMapEntryFnc {
|
|
for _,cf := range s.ifaceCheckers {
|
|
if fn := cf(rtype); fn != nil {
|
|
return fn
|
|
}
|
|
}
|
|
return nil
|
|
}
|