diff --git a/cmd/drop.go b/cmd/drop.go index 658cb7c..a268d20 100644 --- a/cmd/drop.go +++ b/cmd/drop.go @@ -42,18 +42,17 @@ var dropCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { log := args[0] title := args[1] - e := models.PartialEntry() + ms := models.Metas{} if len(j.RawMessage) > 8 { - err := json.Unmarshal([]byte(j.RawMessage), &e) + err := json.Unmarshal([]byte(j.RawMessage), &ms) if err != nil { return err } } for k, v := range fields { - e.Fields = append(e.Fields, models.Meta{k, tools.ParseString(v)}) + ms = append(ms, models.Meta{k, tools.ParseString(v)}) } - e.Title = title - e.Date = d.t + e := models.Entry{title, d.t, ms} l := models.Log{log, []models.Entry{e}} err := files.Append(l) if err != nil { diff --git a/models/entry.go b/models/entry.go index 5a19933..bca992c 100644 --- a/models/entry.go +++ b/models/entry.go @@ -16,14 +16,9 @@ import ( const DateFormat = tools.DateFormat type Entry struct { - Title string - Date time.Time - Fields []Meta - skipMissing bool -} - -func PartialEntry() Entry { - return Entry{skipMissing: true} + Title string + Date time.Time + Fields Metas } type metaRes struct { @@ -42,9 +37,9 @@ func (e Entry) getFieldMarshalChan() chan metaRes { defer wg.Done() if m.Key == "json" { if j, ok := m.Value.(json.RawMessage); ok { - sub := Entry{skipMissing: true} + sub := Metas{} json.Unmarshal(j, &sub) - for _, subM := range sub.Fields { + for _, subM := range sub { o, er := subM.MarshalText() ch <- metaRes{o, er} } @@ -160,9 +155,9 @@ func (e *Entry) getFieldUnarshalChan(in []byte) chan Meta { if err == nil { if m.Key == "json" { if j, ok := m.Value.(json.RawMessage); ok { - sub := Entry{skipMissing: true} - json.Unmarshal(j, &sub) - for _, subM := range sub.Fields { + ms := Metas{} + json.Unmarshal(j, &ms) + for _, subM := range ms { ch <- subM } } @@ -191,26 +186,8 @@ func (e Entry) MarshalJSON() ([]byte, error) { out := map[string]any{} out["title"] = e.Title out["date"] = e.Date.Format(time.RFC3339) - for _, f := range e.Fields { - if _, ok := out[f.Key]; !ok { - if f.Key == "json" { - ob := map[string]any{} - if j, ok := f.Value.(json.RawMessage); ok { - json.Unmarshal(j, &ob) - } - // If we couldn't get valid data from there, this will just be empty - for k, v := range ob { - if k != "title" && k != "date" { - out[k] = v - } - } - } else { - out[f.Key] = f.Value - if vt, ok := f.Value.(time.Time); ok { - out[f.Key] = vt.Format(time.RFC3339) - } - } - } + for k, v := range e.Fields.toMap() { + out[k] = v } return json.Marshal(out) } @@ -259,16 +236,16 @@ func (e *Entry) UnmarshalJSON(in []byte) error { return newParsingError(err) } title, ok := out["title"].(string) - if (!ok || title == "") && !e.skipMissing { + if !ok || title == "" { return ErrorMissingTitle } e.Title = title dates, ok := out["date"].(string) - if (!ok || dates == "") && !e.skipMissing { + if !ok || dates == "" { return ErrorMissingDate } date, err := tools.ParseDate(dates) - if err != nil && !e.skipMissing { + if err != nil { return newParsingError(err) } e.Date = date diff --git a/models/entry_test.go b/models/entry_test.go index aefecc9..01c65a7 100644 --- a/models/entry_test.go +++ b/models/entry_test.go @@ -9,7 +9,6 @@ import ( "time" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) // Type assertions @@ -18,19 +17,6 @@ var _ encoding.TextUnmarshaler = new(Entry) var _ json.Marshaler = Entry{} var _ json.Unmarshaler = new(Entry) -func TestPartialEntry(t *testing.T) { - e := PartialEntry() - assert.True(t, e.skipMissing) - err := json.Unmarshal([]byte(`{"a":42}`), &e) - assert.NoError(t, err) - assert.Equal(t, "", e.Title) - assert.Equal(t, time.Time{}, e.Date) - require.Len(t, e.Fields, 1) - f := e.Fields[0] - assert.Equal(t, "a", f.Key) - assert.Equal(t, int64(42), f.Value) -} - func TestEntryMarshal(t *testing.T) { when := time.Now() whens := when.Format(DateFormat) @@ -84,7 +70,7 @@ func TestEntryMarshal(t *testing.T) { func getEntryMarshalTestRunner(title string, date time.Time, fields []Meta, first string, lines []string, err error) func(*testing.T) { return func(t *testing.T) { - en := Entry{title, date, fields, false} + en := Entry{title, date, fields} o, er := en.MarshalText() assert.Equal(t, err, er) if first == "" { @@ -232,7 +218,7 @@ func TestEntryJsonMarshal(t *testing.T) { func getEntryJsonMarshalTestRunner(title string, date time.Time, fields []Meta, out string, err error) func(t *testing.T) { return func(t *testing.T) { - e := Entry{title, date, fields, false} + e := Entry{title, date, fields} o, er := json.Marshal(e) if err == nil { assert.JSONEq(t, out, string(o)) diff --git a/models/meta_test.go b/models/meta_test.go index 1e0888b..3f623d0 100644 --- a/models/meta_test.go +++ b/models/meta_test.go @@ -97,3 +97,31 @@ func getMetaTestRunner(key string, value any, out string, err error, newVal any) } } } + +func TestMetasJson(t *testing.T) { + ms := Metas{{"me", 41}, {"you", false}} + exp := `{"me":41,"you":false}` + o, err := json.Marshal(ms) + assert.NoError(t, err) + assert.JSONEq(t, exp, string(o)) +} + +func TestMetasJsonUnmarshal(t *testing.T) { + ms := Metas{} + in := `{"me":"cool","you":false}` + err := json.Unmarshal([]byte(in), &ms) + assert.NoError(t, err) + assert.Len(t, ms, 2) + assert.ElementsMatch(t, Metas{ + {"me", "cool"}, + {"you", false}, + }, ms) +} + +func TestMetasJsonError(t *testing.T) { + ms := Metas{} + in := "not json" + err := (&ms).UnmarshalJSON([]byte(in)) + assert.Error(t, err) + assert.Len(t, ms, 0) +} diff --git a/models/metas.go b/models/metas.go new file mode 100644 index 0000000..7a2602f --- /dev/null +++ b/models/metas.go @@ -0,0 +1,64 @@ +package models + +import ( + //"bufio" + //"bytes" + "encoding/json" + //"fmt" + //"errors" + //"regexp" + //"strings" + //"sync" + "time" + //"codeberg.org/danjones000/my-log/tools" +) + +type Metas []Meta + +func (ms Metas) toMap() map[string]any { + out := map[string]any{} + for _, f := range ms { + if _, found := out[f.Key]; found || f.Key == "title" || f.Key == "date" { + continue + } + if f.Key == "json" { + ob := map[string]any{} + if j, ok := f.Value.(json.RawMessage); ok { + json.Unmarshal(j, &ob) + } + // If we couldn't get valid data from there, this will just be empty + for k, v := range ob { + if k != "title" && k != "date" { + out[k] = v + } + } + } else { + out[f.Key] = f.Value + if vt, ok := f.Value.(time.Time); ok { + out[f.Key] = vt.Format(time.RFC3339) + } + } + } + return out +} + +func (ms Metas) MarshalJSON() ([]byte, error) { + return json.Marshal(ms.toMap()) +} + +func (ms *Metas) UnmarshalJSON(in []byte) error { + old := (*ms).toMap() + out := map[string]any{} + err := json.Unmarshal(in, &out) + if err != nil { + return err + } + ret := *ms + for k, v := range out { + if _, found := old[k]; k != "title" && k != "date" && !found { + ret = append(ret, Meta{k, v}) + } + } + *ms = ret + return nil +}