From 0da5efcafe38701e686eab7e134788c7c5dce000 Mon Sep 17 00:00:00 2001 From: Dan Jones Date: Mon, 29 Jan 2024 22:07:18 -0600 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Entry=20implements=20json.Unmarshal?= =?UTF-8?q?er?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- models/entry.go | 46 +++++++++++++++++++++++++++++++++++++++++++- models/entry_test.go | 13 ++++++++----- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/models/entry.go b/models/entry.go index ad4db9d..17f94c4 100644 --- a/models/entry.go +++ b/models/entry.go @@ -179,6 +179,50 @@ func (e Entry) MarshalJSON() ([]byte, error) { return json.Marshal(out) } -func (e *Entry) UnmarshalJSON([]byte) error { +func (e *Entry) UnmarshalJSON(in []byte) error { + out := map[string]any{} + dec := json.NewDecoder(bytes.NewReader(in)) + dec.UseNumber() + err := dec.Decode(&out) + if err != nil { + return newParsingError(err) + } + title, ok := out["title"].(string) + if !ok || title == "" { + return ErrorMissingTitle + } + e.Title = title + delete(out, "title") + dates, ok := out["date"].(string) + if !ok || dates == "" { + return ErrorMissingDate + } + date, err := time.Parse(time.RFC3339, dates) + if err != nil { + return newParsingError(err) + } + e.Date = date + delete(out, "date") + for k, v := range out { + m := Meta{Key: k} + if vs, ok := v.(string); ok { + if vd, err := time.Parse(time.RFC3339, vs); err == nil { + m.Value = vd + } else { + m.Value = vs + } + } else if n, ok := v.(json.Number); ok { + it, _ := n.Int64() + fl, _ := n.Float64() + if float64(it) == fl { + m.Value = it + } else { + m.Value = fl + } + } else { + m.Value = v + } + e.Fields = append(e.Fields, m) + } return nil } diff --git a/models/entry_test.go b/models/entry_test.go index 5c78568..f5d017c 100644 --- a/models/entry_test.go +++ b/models/entry_test.go @@ -233,7 +233,7 @@ func getEntryJsonMarshalTestRunner(title string, date time.Time, fields []Meta, } func TestEntryJsonUnmarshal(t *testing.T) { - when := time.Now() + when := time.Now().Truncate(time.Second) whens := when.Format(time.RFC3339) simple := []Meta{} tests := []struct { @@ -250,6 +250,7 @@ func TestEntryJsonUnmarshal(t *testing.T) { {"empty-title", `{"title":"","date":"` + whens + `"}`, "", when, simple, ErrorMissingTitle}, {"empty-date", `{"title":"A Title","date":""}`, "", when, simple, ErrorMissingDate}, {"bad-date", `{"title":"A Title","date":"bad"}`, "", when, simple, ErrorParsing}, + {"bad-json", `{"title":"A Title","date":"`, "", when, simple, ErrorParsing}, { "single-field", `{"title":"A Title","date":"` + whens + `","hello":"Hi"}`, @@ -260,10 +261,10 @@ func TestEntryJsonUnmarshal(t *testing.T) { }, { "many-fields", - `{"title":"A Title","date":"` + whens + `","hello":"Hi","bye":42,"b":true}`, + `{"title":"A Title","date":"` + whens + `","hello":"Hi","bye":42,"b":true,"fl":42.13}`, "A Title", when, - []Meta{{"hello", "Hi"}, {"bye", 42}, {"b", true}}, + []Meta{{"hello", "Hi"}, {"bye", int64(42)}, {"b", true}, {"fl", float64(42.13)}}, nil, }, { @@ -284,14 +285,16 @@ func TestEntryJsonUnmarshal(t *testing.T) { func getEntryJsonUnmarshalTestRunner(in, title string, date time.Time, fields []Meta, err error) func(t *testing.T) { return func(t *testing.T) { e := new(Entry) - er := json.Unmarshal([]byte(in), e) + er := e.UnmarshalJSON([]byte(in)) if err != nil { assert.ErrorIs(t, er, err) return } + assert.Nil(t, er) assert.Equal(t, title, e.Title) assert.WithinRange(t, e.Date, date.Add(-time.Second), date.Add(time.Second)) + assert.Len(t, e.Fields, len(fields)) for _, f := range fields { got := false for _, m := range e.Fields { @@ -308,7 +311,7 @@ func getEntryJsonUnmarshalTestRunner(in, title string, date time.Time, fields [] break } } - assert.Truef(t, got, "Couldn't find field %+v", f) + assert.Truef(t, got, "Couldn't find field %+v. We have %+v", f, e.Fields) } } }