// ------------------------------------------------------------------------ // 🚀 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 }