From 6095c2497ebcc6f566a5ede6110960faff1e7b64 Mon Sep 17 00:00:00 2001 From: Dan Jones Date: Thu, 12 Feb 2026 10:41:35 -0600 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20support=20for=20mixed-level?= =?UTF-8?q?=20nested=20keys=20with=20dot/blank=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Handle @parent:child and @parent:child: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 --- models/entry_test.go | 29 +++++++++++++++++++++++++++++ models/meta.go | 8 ++++++-- models/metas.go | 21 ++++++++++++++++++--- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/models/entry_test.go b/models/entry_test.go index a4fd2b7..ecfc303 100644 --- a/models/entry_test.go +++ b/models/entry_test.go @@ -88,6 +88,24 @@ func TestEntryMarshal(t *testing.T) { []string{"@me:age 43", "@me:name:first Dan", "@me:name:last Jones"}, nil, }, + { + "double-nested-map-dot", + "Title DM", + when, + []Meta{{"me", map[string]any{"age": 43, "name": map[string]any{".": "Dan Jones", "nick": "Danny"}}}}, + "@begin " + whens + " - Title DM", + []string{"@me:age 43", "@me:name Dan Jones", "@me:name:nick Danny"}, + nil, + }, + { + "double-nested-map-blank", + "Title DM", + when, + []Meta{{"me", map[string]any{"age": 43, "name": map[string]any{"": "Dan Jones", "nick": "Danny"}}}}, + "@begin " + whens + " - Title DM", + []string{"@me:age 43", "@me:name Dan Jones", "@me:name:nick Danny"}, + nil, + }, { "nested-keys-in-json", "Title NKJ", @@ -169,6 +187,14 @@ func TestEntryUnmarshal(t *testing.T) { []Meta{{"me:name", "Dan"}, {"me:coder", true}}, nil, }, + { + "nested-field-dot", + "@begin " + whens + " - A Title\n@me:name Dan Jones\n@me:name:nick Danny\n@me:coder true @end", + "A Title", + when, + []Meta{{"me:name", "Dan Jones"}, {"me:name:nick", "Danny"}, {"me:coder", true}}, + nil, + }, { "json-field", "@begin " + whens + " - Some Guy\n" + `@json {"name":"Dan","coder":true} @end`, @@ -256,6 +282,9 @@ func TestEntryJsonMarshal(t *testing.T) { {"nested-field", "A Title", when, []Meta{{"obj:foo", "bar"}, {"obj:title", "Sub-title"}}, `{"title":"A Title","date":"` + whens + `","obj":{"foo":"bar","title":"Sub-title"}}`, nil}, {"double-nested-field", "A Title", when, []Meta{{"obj:foo", "bar"}, {"obj:me:name", "Dan"}, {"obj:me:age", 27}}, `{"title":"A Title","date":"` + whens + `","obj":{"foo":"bar","me":{"name":"Dan","age":27}}}`, nil}, {"nested-plus-json", "A Title", when, []Meta{{"obj:foo", "bar"}, {"obj:me", json.RawMessage(`{"name":"Dan","age":27}`)}}, `{"title":"A Title","date":"` + whens + `","obj":{"foo":"bar","me":{"name":"Dan","age":27}}}`, nil}, + {"nested-part", "A Title", when, []Meta{{"obj:foo", "bar"}, {"obj:me", "Dan"}, {"obj:me:age", 27}}, `{"title":"A Title","date":"` + whens + `","obj":{"foo":"bar","me":{".":"Dan","age":27}}}`, nil}, + {"nested-part-order", "A Title", when, []Meta{{"obj:foo", "bar"}, {"obj:me:age", 27}, {"obj:me", "Dan"}}, `{"title":"A Title","date":"` + whens + `","obj":{"foo":"bar","me":{".":"Dan","age":27}}}`, nil}, + {"nested-part-order-two", "A Title", when, []Meta{{"obj:foo", "bar"}, {"obj:me:age", 27}, {"obj:me", "Dan"}, {"obj:me:cool", true}}, `{"title":"A Title","date":"` + whens + `","obj":{"foo":"bar","me":{".":"Dan","age":27,"cool":true}}}`, nil}, } for _, tt := range tests { diff --git a/models/meta.go b/models/meta.go index c35f302..15ed90b 100644 --- a/models/meta.go +++ b/models/meta.go @@ -50,12 +50,16 @@ func marshalMap(pre string, mp map[string]any, buff *bytes.Buffer) error { buff.WriteRune('\n') } idx++ + newKey := pre + ":" + k + if k == "." || k == "" { + newKey = pre + } if subM, ok := v.(map[string]any); ok { - if err := marshalMap(pre+":"+k, subM, buff); err != nil { + if err := marshalMap(newKey, subM, buff); err != nil { return err } } else { - mSub := Meta{pre + ":" + k, v} + mSub := Meta{newKey, v} if err := mSub.marshalToBuff(buff); err != nil { return err } diff --git a/models/metas.go b/models/metas.go index 5b68455..92a9cec 100644 --- a/models/metas.go +++ b/models/metas.go @@ -42,6 +42,7 @@ func (ms Metas) Map() map[string]any { } func parseNestedFields(f map[string]any) { + todelete := make([]string, 0, len(f)) for k, v := range f { if strings.Contains(k, ":") { idx := strings.Index(k, ":") @@ -50,14 +51,28 @@ func parseNestedFields(f map[string]any) { nest, ok := f[top].(map[string]any) if !ok { - nest = map[string]any{} + curr := f[top] + if curr == nil { + nest = map[string]any{} + } else { + nest = map[string]any{".": curr} + } + } + + curr, ok := nest[bottom].(map[string]any) + if ok { + curr["."] = v + } else { + nest[bottom] = v } - nest[bottom] = v parseNestedFields(nest) f[top] = nest - delete(f, k) + todelete = append(todelete, k) } } + for _, k := range todelete { + delete(f, k) + } } // Implements json.Marshaler