- Handle @parent:child and @parent🧒grandchild coexistence
- Use '.' key for parent values when nested children exist
- Add comprehensive test cases for double-nested scenarios
- Support both '.' and '' as special keys for parent values
109 lines
2.1 KiB
Go
109 lines
2.1 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++
|
|
newKey := pre + ":" + k
|
|
if k == "." || k == "" {
|
|
newKey = pre
|
|
}
|
|
if subM, ok := v.(map[string]any); ok {
|
|
if err := marshalMap(newKey, subM, buff); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
mSub := Meta{newKey, 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
|
|
}
|