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.
621 lines
12 KiB
621 lines
12 KiB
// ------------------------------------------------------------------------
|
|
// 🚀 Project active3
|
|
// 🔗 Active Thing (activething.com) git.activething.com/go
|
|
//
|
|
// File name builder.go
|
|
// ✍️ Created by DEV
|
|
// 📅 Modified 28/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 (
|
|
"apacks"
|
|
"apacks/pkstate"
|
|
"blds"
|
|
"cdcs/cjson"
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"rsls"
|
|
"time"
|
|
)
|
|
|
|
type (
|
|
|
|
Builder struct {
|
|
*pkstate.State
|
|
e *json.Encoder
|
|
f apack.Flags
|
|
}
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func NewBuilder(flag ...apack.Flags) *Builder {
|
|
f := apack.Flags(0)
|
|
if len(flag) == 1 {
|
|
f = flag[0]
|
|
} else if len(flag) > 1 {
|
|
for _,fl := range flag {
|
|
f|=fl
|
|
}
|
|
}
|
|
return &Builder{
|
|
State : pkstate.AcquireState(),
|
|
f : f,
|
|
}
|
|
}
|
|
|
|
|
|
func (b *Builder) release () {
|
|
pkstate.ReleaseState(b.State)
|
|
b.State = nil
|
|
}
|
|
|
|
|
|
|
|
func (b *Builder) Pack(value any) (rs rsls.Bytes) {
|
|
d := false
|
|
|
|
// Defer function to handle panic recovery and resource release
|
|
defer func(bl *Builder, dn *bool){
|
|
// If not done, recover from any panic
|
|
if !(*dn) {
|
|
if rc := recover(); rc != nil {
|
|
if er, is := rc.(error); is {
|
|
rs = rsls.MakeBytesError(er)
|
|
} else {
|
|
rs = rsls.MakeBytesError(fmt.Errorf("%#v", rc))
|
|
}
|
|
}
|
|
}
|
|
// Release resources
|
|
bl.release()
|
|
}(b, &d)
|
|
|
|
// Attempt to process the value
|
|
b.Any(value)
|
|
d = true // Mark as done if no panic occurred
|
|
|
|
// Check for errors in the builder
|
|
if b.Err != nil {
|
|
return rsls.MakeBytesError(b.Err)
|
|
}
|
|
|
|
// Return success result with bytes
|
|
return rsls.MakeBytesSuccess(b.B.Bytes())
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *Builder) Any (value any) {
|
|
|
|
if value == nil {
|
|
if !b.preValue() {
|
|
return
|
|
}
|
|
b.B.W(cjson.G.NullValue)
|
|
b.postValue()
|
|
return
|
|
}
|
|
|
|
if bl,is := value.(blds.Buildable); is {
|
|
b.Buildable(bl)
|
|
return
|
|
}
|
|
|
|
if ms,is := value.(json.Marshaler); is {
|
|
b.Marshaller(ms)
|
|
return
|
|
}
|
|
|
|
switch v := value.(type) {
|
|
|
|
case string:
|
|
b.String(v)
|
|
|
|
case int8:
|
|
b.Int64(int64(v))
|
|
case int16:
|
|
b.Int64(int64(v))
|
|
case int32:
|
|
b.Int64(int64(v))
|
|
case int64:
|
|
b.Int64(v)
|
|
|
|
case uint8:
|
|
b.Uint64(uint64(v))
|
|
case uint16:
|
|
b.Uint64(uint64(v))
|
|
case uint32:
|
|
b.Uint64(uint64(v))
|
|
case uintptr:
|
|
b.Uint64(uint64(v))
|
|
case uint64:
|
|
b.Uint64(v)
|
|
|
|
case float32:
|
|
b.float(float64(v), 32)
|
|
case float64:
|
|
b.float(v, 64)
|
|
|
|
case bool:
|
|
b.Bool(v)
|
|
|
|
case time.Time:
|
|
b.Time(v)
|
|
case time.Duration:
|
|
b.Int64(int64(v))
|
|
|
|
case error:
|
|
b.Error(v)
|
|
|
|
case []bool:
|
|
packListG[bool](b, v, b.Bool)
|
|
case []string:
|
|
packListG[string](b, v, b.String)
|
|
case []int:
|
|
packListG[int](b, v, b.Int)
|
|
case []int8:
|
|
packListG[int8](b, v, b.Int8)
|
|
case []int16:
|
|
packListG[int16](b, v, b.Int16)
|
|
case []int32:
|
|
packListG[int32](b, v, b.Int32)
|
|
case []int64:
|
|
packListG[int64](b, v, b.Int64)
|
|
case []uint:
|
|
packListG[uint](b, v, b.Uint)
|
|
case []uint8:
|
|
packListG[uint8](b, v, b.Uint8)
|
|
case []uint16:
|
|
packListG[uint16](b, v, b.Uint16)
|
|
case []uint32:
|
|
packListG[uint32](b, v, b.Uint32)
|
|
case []uint64:
|
|
packListG[uint64](b, v, b.Uint64)
|
|
case []uintptr:
|
|
packListG[uintptr](b, v, b.Uintptr)
|
|
case []time.Time:
|
|
packListG[time.Time](b, v, b.Time)
|
|
case []time.Duration:
|
|
packListG[time.Duration](b, v, b.Duration)
|
|
case []error:
|
|
b.buildLevel(pkstate.ModeList, v == nil, len(v) == 0, func(bl *Builder) {
|
|
for _, e := range v {
|
|
bl.Item()
|
|
if bl.Err == nil {
|
|
bl.Error(e)
|
|
}
|
|
if bl.Err != nil {
|
|
return
|
|
}
|
|
}
|
|
})
|
|
|
|
case map[string]bool:
|
|
packMapG[bool](b, v, b.Bool)
|
|
case map[string]string:
|
|
packMapG[string](b, v, b.String)
|
|
case map[string]int:
|
|
packMapG[int](b, v, b.Int)
|
|
case map[string]int8:
|
|
packMapG[int8](b, v, b.Int8)
|
|
case map[string]int16:
|
|
packMapG[int16](b, v, b.Int16)
|
|
case map[string]int32:
|
|
packMapG[int32](b, v, b.Int32)
|
|
case map[string]int64:
|
|
packMapG[int64](b, v, b.Int64)
|
|
case map[string]uint:
|
|
packMapG[uint](b, v, b.Uint)
|
|
case map[string]uint8:
|
|
packMapG[uint8](b, v, b.Uint8)
|
|
case map[string]uint16:
|
|
packMapG[uint16](b, v, b.Uint16)
|
|
case map[string]uint32:
|
|
packMapG[uint32](b, v, b.Uint32)
|
|
case map[string]uint64:
|
|
packMapG[uint64](b, v, b.Uint64)
|
|
case map[string]uintptr:
|
|
packMapG[uintptr](b, v, b.Uintptr)
|
|
case map[string]time.Time:
|
|
packMapG[time.Time](b, v, b.Time)
|
|
case map[string]time.Duration:
|
|
packMapG[time.Duration](b, v, b.Duration)
|
|
case map[string]error:
|
|
b.buildLevel(pkstate.ModeMap, v == nil, len(v) == 0, func(bl *Builder) {
|
|
for k, v := range v {
|
|
bl.Field(k)
|
|
if bl.Err == nil {
|
|
bl.Error(v)
|
|
}
|
|
if b.Err != nil {
|
|
return
|
|
}
|
|
}
|
|
})
|
|
|
|
case reflect.Value:
|
|
g.rpackers.getRPacker(v.Type())(b,v)
|
|
|
|
default:
|
|
rv := reflect.ValueOf(value)
|
|
g.rpackers.getRPacker(rv.Type())(b,rv)
|
|
}
|
|
|
|
|
|
}
|
|
|
|
func (b *Builder) Raw (value []byte) {
|
|
if !b.preValue() {
|
|
return
|
|
}
|
|
if value == nil {
|
|
b.B.W(cjson.G.NullValue)
|
|
} else if len(value) == 0 {
|
|
b.B.W(cjson.G.EmptyValue)
|
|
} else {
|
|
b.B.W(value)
|
|
}
|
|
b.postValue()
|
|
}
|
|
|
|
func (b *Builder) Binary(value []byte) {
|
|
if !b.preValue() {
|
|
return
|
|
}
|
|
b.writeBinary(value,b.f)
|
|
b.postValue()
|
|
}
|
|
|
|
func (b *Builder) String (value string) {
|
|
if !b.preValue() {
|
|
return
|
|
}
|
|
b.writeString(value,b.f)
|
|
b.postValue()
|
|
}
|
|
|
|
func (b *Builder) Bool (value bool) {
|
|
if !b.preValue() { return }
|
|
cjson.WriteBool(b.B,value)
|
|
b.postValue()
|
|
}
|
|
|
|
func (b *Builder) Int (value int ){ b.Int64(int64(value)) }
|
|
func (b *Builder) Int8 (value int8 ){ b.Int64(int64(value)) }
|
|
func (b *Builder) Int16 (value int16 ){ b.Int64(int64(value)) }
|
|
func (b *Builder) Int32 (value int32 ){ b.Int64(int64(value)) }
|
|
func (b *Builder) Int64 (value int64 ){
|
|
if !b.preValue() {
|
|
return
|
|
}
|
|
cjson.WriteInt(b.B,value)
|
|
b.postValue()
|
|
}
|
|
|
|
func (b *Builder) Uint (value uint ){ b.Uint64(uint64(value)) }
|
|
func (b *Builder) Uint8 (value uint8 ){ b.Uint64(uint64(value)) }
|
|
func (b *Builder) Uint16 (value uint16 ){ b.Uint64(uint64(value)) }
|
|
func (b *Builder) Uint32 (value uint32 ){ b.Uint64(uint64(value)) }
|
|
func (b *Builder) Uintptr (value uintptr){ b.Uint64(uint64(value)) }
|
|
func (b *Builder) Uint64 (value uint64 ){
|
|
if !b.preValue() {
|
|
return
|
|
}
|
|
cjson.WriteUint(b.B,value)
|
|
b.postValue()
|
|
}
|
|
|
|
func (b *Builder) Float32 (value float32){
|
|
b.float(float64(value),32) }
|
|
|
|
func (b *Builder) Float64 (value float64){
|
|
b.float(value,64) }
|
|
|
|
func (b *Builder) float (value float64, bits int) {
|
|
if !b.preValue() {
|
|
return
|
|
}
|
|
cjson.WriteFloat(b.B,value,bits)
|
|
b.postValue()
|
|
}
|
|
|
|
func (b *Builder) Time (value time.Time) {
|
|
if !b.preValue() {
|
|
return
|
|
}
|
|
cjson.WriteTime(b.B,value,b.f.DateFormat())
|
|
b.postValue()
|
|
}
|
|
|
|
func (b *Builder) Duration (value time.Duration) { b.Int64(int64(value)) }
|
|
|
|
func (b *Builder) Error (value error) {
|
|
if !b.preValue() {
|
|
return
|
|
}
|
|
if value == nil {
|
|
b.B.W(cjson.G.NullValue)
|
|
} else {
|
|
b.writeString(value.Error(), b.f|apack.FlagScapeHTML)
|
|
}
|
|
b.postValue()
|
|
}
|
|
|
|
func (b *Builder) Buildable (value blds.Buildable) {
|
|
if !b.preValue() {
|
|
return
|
|
}
|
|
if value == nil {
|
|
|
|
} else {
|
|
value.Build(b)
|
|
}
|
|
b.postValue()
|
|
}
|
|
|
|
func (b *Builder) Marshaller (value json.Marshaler) {
|
|
if !b.preValue() {
|
|
return
|
|
}
|
|
if value == nil {
|
|
b.B.W(cjson.G.NullValue)
|
|
} else {
|
|
dt, er := value.MarshalJSON()
|
|
if b.SetError(er) {
|
|
return
|
|
}
|
|
b.B.W(dt)
|
|
}
|
|
b.postValue()
|
|
}
|
|
|
|
|
|
|
|
func (b *Builder) writeString (value string, flags apack.Flags) {
|
|
if len(value) == 0 {
|
|
b.B.W(cjson.G.EmptyValue)
|
|
return
|
|
}
|
|
if flags.Encrypted() {
|
|
b.writeEncrypted([]byte(value),flags)
|
|
return
|
|
}
|
|
if flags.Binary() {
|
|
b.writeBinary([]byte(value),flags)
|
|
return
|
|
}
|
|
cjson.WriteString(b.B,value,flags.EscapeHTML())
|
|
}
|
|
|
|
func (b *Builder) writeEncrypted(value []byte, flags apack.Flags) {
|
|
if b.Encrypter == nil {
|
|
b.SetError(apack.G.ErrInvalidEncrypter)
|
|
return
|
|
}
|
|
rs := b.Encrypter.Encrypt(value)
|
|
if !rs.Ok() {
|
|
b.SetError(rs.Fail())
|
|
return
|
|
}
|
|
b.writeBinary(rs.Value(),flags)
|
|
}
|
|
|
|
func (b *Builder) writeBinary (value []byte, flags apack.Flags) {
|
|
if flags.Bin32() {
|
|
b.SetError(cjson.WriteBase32(b.B,value))
|
|
return
|
|
}
|
|
b.SetError(cjson.WriteBase64(b.B,value))
|
|
}
|
|
|
|
|
|
|
|
func (b *Builder) List (fnc func(builder blds.ListBuilder)) {
|
|
b.buildLevel(pkstate.ModeList,fnc==nil,false,func(bl *Builder){
|
|
fnc(b) }) }
|
|
|
|
|
|
func (b *Builder) Map (fnc func(builder blds.MapBuilder)) {
|
|
b.buildLevel(pkstate.ModeMap,fnc == nil, false, func(bl *Builder){
|
|
fnc(b) }) }
|
|
|
|
|
|
func (b *Builder) Object (fnc func(builder blds.ObjectBuilder)) {
|
|
b.buildLevel(pkstate.ModeObject,fnc == nil, false, func(bl *Builder){
|
|
fnc(b) }) }
|
|
|
|
|
|
|
|
func (b *Builder) Item () blds.ValueBuilder {
|
|
if b.preElem() {
|
|
b.Current.IsValue = true
|
|
}
|
|
return b
|
|
}
|
|
|
|
func (b *Builder) Key (key any) blds.ValueBuilder {
|
|
if key == nil {
|
|
b.SetError(apack.G.ErrInvalidKey.WithMessage("key is null"))
|
|
return b
|
|
}
|
|
rs := g.stringer.getStringer(key)
|
|
if !rs.Ok() {
|
|
b.SetError(apack.G.ErrInvalidKey.WithCause(rs.Fail()))
|
|
return b
|
|
}
|
|
return b.Field(rs.Value())
|
|
}
|
|
|
|
func (b *Builder) Field (name string) blds.ValueBuilder {
|
|
if len(name) == 0 {
|
|
b.SetError(apack.G.ErrInvalidKey.WithMessage("key is empty"))
|
|
return b
|
|
}
|
|
if b.preElem() {
|
|
b.writeString(name,apack.FlagScapeHTML)
|
|
}
|
|
return b.postElem()
|
|
}
|
|
|
|
|
|
|
|
func (b *Builder) buildLevel (mode pkstate.Mode, isNil, isEmpty bool, buildFnc func(builder *Builder)) {
|
|
if !b.preValue() {
|
|
return
|
|
}
|
|
|
|
if isNil {
|
|
b.B.W(cjson.G.NullValue)
|
|
b.postValue()
|
|
return
|
|
}
|
|
|
|
if isEmpty {
|
|
if mode == pkstate.ModeList {
|
|
b.B.Write2Byte('[',']')
|
|
} else {
|
|
b.B.Write2Byte('{','}')
|
|
}
|
|
b.postValue()
|
|
return
|
|
}
|
|
|
|
b.Open(mode)
|
|
be := byte('}')
|
|
if mode == pkstate.ModeList {
|
|
b.B.WriteByte('[')
|
|
be = ']'
|
|
} else {
|
|
b.B.WriteByte('{')
|
|
}
|
|
buildFnc(b)
|
|
if b.Err != nil {
|
|
return
|
|
}
|
|
|
|
b.B.WriteByte(be)
|
|
b.Close(mode)
|
|
b.postValue()
|
|
}
|
|
|
|
|
|
|
|
func (b *Builder) preValue () bool {
|
|
if b.Err != nil {
|
|
panic(apack.G.ErrInvalidState.WithCause(b.Err))
|
|
}
|
|
if !b.Current.IsValue {
|
|
_ = b.SetError(apack.G.ErrStateInvalidElem)
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
|
|
func (b *Builder) postValue () {
|
|
if b.Err != nil {
|
|
return
|
|
}
|
|
b.Current.IsValue = false
|
|
b.Current.Count++
|
|
if b.Current.Mode == pkstate.ModeValue {
|
|
b.Current.Mode = pkstate.ModeNone
|
|
}
|
|
}
|
|
|
|
|
|
func (b *Builder) preElem () bool {
|
|
if b.Err != nil {
|
|
panic(apack.G.ErrInvalidState.WithCause(b.Err))
|
|
}
|
|
if b.Current.IsValue {
|
|
_ = b.SetError(apack.G.ErrStateInvalidElem)
|
|
return false
|
|
}
|
|
if b.Current.Count > 0 {
|
|
b.B.WriteByte(',')
|
|
}
|
|
return true
|
|
}
|
|
|
|
|
|
func (b *Builder) postElem () *Builder {
|
|
if b.Err == nil {
|
|
b.Current.IsValue = true
|
|
b.B.WriteByte(':')
|
|
}
|
|
return b
|
|
}
|
|
|
|
func (b *Builder) encode (value any) error {
|
|
if b.e == nil {
|
|
b.e = json.NewEncoder(b.B)
|
|
}
|
|
return b.e.Encode(value)
|
|
}
|
|
|
|
|
|
|
|
|
|
func packListG[T basicTypes](b *Builder, list []T, fncType func(vl T)) {
|
|
b.buildLevel(pkstate.ModeList,list == nil, len(list) == 0,func(bl *Builder){
|
|
for _,v := range list {
|
|
b.Item()
|
|
if b.Err == nil {
|
|
fncType(v)
|
|
}
|
|
if bl.Err != nil {
|
|
return
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func packMapG[T basicTypes](b *Builder, m map[string]T, fncType func(vl T)) {
|
|
b.buildLevel(pkstate.ModeMap, m == nil, len(m) == 0, func(bl *Builder){
|
|
for k,v := range m {
|
|
// Usamos b.Field y no b.Key porque sabemos que no es necesario
|
|
// hacerle un stringify y el objeto como el map la codificación
|
|
// json es la misma
|
|
b.Field(k)
|
|
if b.Err == nil {
|
|
fncType(v)
|
|
}
|
|
if b.Err != nil {
|
|
return
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|