This commit completes the implementation of nested field support.
- :
- now correctly handles and by recursively flattening them into format.
- Introduced for recursive map marshalling.
- Refactored for cleaner buffer writing.
- : Added comprehensive test cases for nested JSON, nested maps, double-nested maps, and nested keys within JSON to ensure correct marshalling and unmarshalling.
- : Updated tests to reflect the new nil handling and removed redundant JSON object test.
This allows for more flexible and structured data representation within log entries.
105 lines
2 KiB
Go
105 lines
2 KiB
Go
package models
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"regexp"
|
|
|
|
"codeberg.org/danjones000/my-log/tools"
|
|
)
|
|
|
|
type Meta struct {
|
|
Key string
|
|
Value any
|
|
}
|
|
|
|
func (m Meta) MarshalText() ([]byte, error) {
|
|
if regexp.MustCompile(`\s`).MatchString(m.Key) {
|
|
return []byte{}, fmt.Errorf("whitespace is not allowed in key: %s", m.Key)
|
|
}
|
|
|
|
buff := &bytes.Buffer{}
|
|
|
|
if jv, ok := m.Value.(map[string]any); ok {
|
|
err := marshalMap(m.Key, jv, buff)
|
|
return buff.Bytes(), err
|
|
}
|
|
|
|
if jj, ok := m.Value.(json.RawMessage); ok {
|
|
mp := map[string]any{}
|
|
err := json.Unmarshal(jj, &mp)
|
|
if err == nil {
|
|
err := marshalMap(m.Key, mp, buff)
|
|
return buff.Bytes(), err
|
|
}
|
|
}
|
|
|
|
if err := m.marshalToBuff(buff); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return buff.Bytes(), nil
|
|
}
|
|
|
|
func marshalMap(pre string, mp map[string]any, buff *bytes.Buffer) error {
|
|
var idx uint
|
|
for k, v := range mp {
|
|
if idx > 0 {
|
|
buff.WriteRune('\n')
|
|
}
|
|
idx++
|
|
if subM, ok := v.(map[string]any); ok {
|
|
if err := marshalMap(pre+":"+k, subM, buff); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
mSub := Meta{pre + ":" + k, v}
|
|
if err := mSub.marshalToBuff(buff); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m Meta) marshalToBuff(buff *bytes.Buffer) error {
|
|
buff.WriteRune('@')
|
|
buff.WriteString(m.Key)
|
|
buff.WriteRune(' ')
|
|
n, err := tools.WriteValue(buff, m.Value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if n == 0 {
|
|
return ErrorParsing
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *Meta) UnmarshalText(in []byte) error {
|
|
if len(in) == 0 {
|
|
return newParsingError(errors.New("Unable to Unmarshal empty string"))
|
|
}
|
|
re := regexp.MustCompile("(?s)^@([^ ]+) (.*)( @end)?$")
|
|
match := re.FindSubmatch(in)
|
|
if len(match) == 0 {
|
|
return newParsingError(fmt.Errorf("Failed to match %s", in))
|
|
}
|
|
m.Key = string(match[1])
|
|
return m.processMeta(match[2])
|
|
}
|
|
|
|
func (m *Meta) processMeta(in []byte) error {
|
|
if len(in) == 0 {
|
|
return newParsingError(errors.New("No value found"))
|
|
}
|
|
v := tools.ParseBytes(in)
|
|
if v == "" {
|
|
return newParsingError(errors.New("No value found"))
|
|
}
|
|
|
|
m.Value = v
|
|
return nil
|
|
}
|