my-log/models/meta.go
Dan Jones 4fc1c623a0 Implement full support for nested fields in Meta and Entry marshalling
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.
2026-02-10 18:15:07 -06:00

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
}