From d1b3604e1ef1b0720a72a2921550759d06734af4 Mon Sep 17 00:00:00 2001 From: Dan Jones Date: Sun, 10 Mar 2024 11:31:07 -0500 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8=20Add=20Formatter.Logs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- formatters/interface.go | 1 + formatters/plain.go | 20 ++++++++++++++++++++ formatters/plain_test.go | 35 +++++++++++++++++++++++++++++++++-- 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/formatters/interface.go b/formatters/interface.go index 4276aed..5478572 100644 --- a/formatters/interface.go +++ b/formatters/interface.go @@ -4,6 +4,7 @@ import "codeberg.org/danjones000/my-log/models" type Formatter interface { Name() string + Logs([]models.Log) (out []byte, err error) Log(models.Log) (out []byte, err error) Entry(models.Entry) (out []byte, err error) Meta(models.Meta) (out []byte, err error) diff --git a/formatters/plain.go b/formatters/plain.go index ed8ee82..2b79f00 100644 --- a/formatters/plain.go +++ b/formatters/plain.go @@ -20,6 +20,26 @@ func (pt *PlainText) Name() string { return "plain" } +func (pt *PlainText) Logs(logs []models.Log) (out []byte, err error) { + if len(logs) == 0 { + return + } + + buff := &bytes.Buffer{} + first := true + for _, log := range logs { + o, _ := pt.Log(log) + if !first { + buff.WriteByte(10) + buff.WriteByte(10) + } + first = false + buff.Write(o) + } + out = buff.Bytes() + return +} + func (pt *PlainText) Log(log models.Log) (out []byte, err error) { if len(log.Entries) == 0 { return diff --git a/formatters/plain_test.go b/formatters/plain_test.go index e384101..487550f 100644 --- a/formatters/plain_test.go +++ b/formatters/plain_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestPlainLog(t *testing.T) { +func TestPlainLogs(t *testing.T) { m := []models.Meta{ {"foo", "bar"}, {"baz", 42}, @@ -23,11 +23,17 @@ func TestPlainLog(t *testing.T) { {Title: "small", Date: time.Now()}, } l := models.Log{"stuff", e} + e2 := models.Entry{ + Title: "three", + Date: time.Now(), + } + l2 := models.Log{"more-stuff", []models.Entry{e2}} + logs := []models.Log{l, l2} f, err := New("plain") require.NoError(t, err) - out, err := f.Log(l) + out, err := f.Logs(logs) require.NoError(t, err) read := bytes.NewReader(out) @@ -67,6 +73,24 @@ func TestPlainLog(t *testing.T) { line = scan.Text() assert.Equal(t, "Date: "+e[1].Date.Format(tools.DateFormat), line) + scan.Scan() + scan.Scan() + line = scan.Text() + assert.Equal(t, l2.Name, line) + + scan.Scan() + line = scan.Text() + assert.Equal(t, "#######", line) + + scan.Scan() + scan.Scan() + line = scan.Text() + assert.Equal(t, "Title: "+e2.Title, line) + + scan.Scan() + line = scan.Text() + assert.Equal(t, "Date: "+e2.Date.Format(tools.DateFormat), line) + more := scan.Scan() assert.False(t, more) } @@ -76,6 +100,13 @@ func TestPlainName(t *testing.T) { assert.Equal(t, "plain", f.Name()) } +func TestPlainLogNone(t *testing.T) { + f, _ := New("plain") + out, err := f.Logs([]models.Log{}) + assert.NoError(t, err) + assert.Len(t, out, 0) +} + func TestPlainLogNoEntries(t *testing.T) { f, _ := New("plain") out, err := f.Log(models.Log{Name: "foo"}) From 4c0edcd1a58a2c39e7043b1646cbe930aa52f9b4 Mon Sep 17 00:00:00 2001 From: Dan Jones Date: Sun, 10 Mar 2024 11:33:51 -0500 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9C=A8=20Add=20JSON=20formatter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- formatters/json.go | 63 ++++++++++++++++++++++++++++++++++++++ formatters/json_test.go | 67 +++++++++++++++++++++++++++++++++++++++++ formatters/new.go | 1 + formatters/new_test.go | 2 +- 4 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 formatters/json.go create mode 100644 formatters/json_test.go diff --git a/formatters/json.go b/formatters/json.go new file mode 100644 index 0000000..b3b2d97 --- /dev/null +++ b/formatters/json.go @@ -0,0 +1,63 @@ +package formatters + +import ( + "encoding/json" + "time" + + "codeberg.org/danjones000/my-log/config" + "codeberg.org/danjones000/my-log/models" + //"codeberg.org/danjones000/my-log/tools" +) + +func newJson(ff config.Formatters) (Formatter, error) { + // @todo pretty print + return &Json{ff.Json()}, nil +} + +type Json struct { + jf config.JsonFormat +} + +func (js *Json) Name() string { + return "json" +} + +func (js *Json) Meta(m models.Meta) ([]byte, error) { + o := map[string]any{m.Key: m.Value} + return json.Marshal(o) +} + +func (js *Json) entryMap(e models.Entry) map[string]any { + o := map[string]any{ + "title": e.Title, + "date": e.Date.Format(time.RFC3339), + } + for _, m := range e.Fields { + o[m.Key] = m.Value + } + return o +} + +func (js *Json) Entry(e models.Entry) ([]byte, error) { + return json.Marshal(js.entryMap(e)) +} + +func (js *Json) Log(l models.Log) ([]byte, error) { + return js.Logs([]models.Log{l}) +} + +func (js *Json) Logs(logs []models.Log) (out []byte, err error) { + if len(logs) == 0 { + return + } + + o := map[string][]map[string]any{} + for _, l := range logs { + es := []map[string]any{} + for _, e := range l.Entries { + es = append(es, js.entryMap(e)) + } + o[l.Name] = es + } + return json.Marshal(o) +} diff --git a/formatters/json_test.go b/formatters/json_test.go new file mode 100644 index 0000000..a81d4ed --- /dev/null +++ b/formatters/json_test.go @@ -0,0 +1,67 @@ +package formatters + +import ( + //"bufio" + //"bytes" + "fmt" + "testing" + "time" + + "codeberg.org/danjones000/my-log/models" + //"codeberg.org/danjones000/my-log/tools" + "github.com/stretchr/testify/assert" + //"github.com/stretchr/testify/require" +) + +func TestJsonName(t *testing.T) { + f, _ := New("json") + assert.Equal(t, "json", f.Name()) +} + +func TestJsonMeta(t *testing.T) { + f, _ := New("json") + m := models.Meta{"foo", 42} + exp := `{"foo":42}` + o, err := f.Meta(m) + assert.NoError(t, err) + assert.JSONEq(t, exp, string(o)) +} + +func TestJsonEntry(t *testing.T) { + when := time.Now() + f, _ := New("json") + m := models.Meta{"foo", 42} + e := models.Entry{ + Title: "Homer", + Date: when, + Fields: []models.Meta{m}, + } + exp := fmt.Sprintf(`{"title":"%s","date":"%s","foo":42}`, e.Title, when.Format(time.RFC3339)) + o, err := f.Entry(e) + assert.NoError(t, err) + assert.JSONEq(t, exp, string(o)) +} + +func TestJsonLog(t *testing.T) { + when := time.Now() + f, _ := New("json") + m := models.Meta{"foo", 42} + e := models.Entry{ + Title: "Homer", + Date: when, + Fields: []models.Meta{m}, + } + l := models.Log{"stuff", []models.Entry{e}} + exp := fmt.Sprintf(`{"%s":[{"title":"%s","date":"%s","foo":42}]}`, l.Name, e.Title, when.Format(time.RFC3339)) + o, err := f.Log(l) + assert.NoError(t, err) + assert.JSONEq(t, exp, string(o)) +} + +func TestJsonNoLogs(t *testing.T) { + f, _ := New("json") + o, err := f.Logs([]models.Log{}) + var exp []byte + assert.NoError(t, err) + assert.Equal(t, exp, o) +} diff --git a/formatters/new.go b/formatters/new.go index d88a9d0..37d8a96 100644 --- a/formatters/new.go +++ b/formatters/new.go @@ -10,6 +10,7 @@ type formatMaker func(config.Formatters) (Formatter, error) var formatterMap = map[string]formatMaker{ "plain": newPlain, + "json": newJson, } func Preferred() (f Formatter, err error) { diff --git a/formatters/new_test.go b/formatters/new_test.go index 04b40fb..c6d5e56 100644 --- a/formatters/new_test.go +++ b/formatters/new_test.go @@ -10,7 +10,7 @@ import ( ) func TestKinds(t *testing.T) { - assert.Equal(t, []string{"plain"}, Kinds()) + assert.ElementsMatch(t, []string{"plain", "json"}, Kinds()) } func TestNewUnsupported(t *testing.T) { From 1962e1db502211815f0ae8f49c3c84de718e2004 Mon Sep 17 00:00:00 2001 From: Dan Jones Date: Sun, 10 Mar 2024 12:14:21 -0500 Subject: [PATCH 3/3] =?UTF-8?q?=E2=9C=A8=20Pretty=20print=20JSON?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- formatters/json.go | 28 +++++++++++++++++++++------- formatters/json_test.go | 22 ++++++++++++++++++---- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/formatters/json.go b/formatters/json.go index b3b2d97..d56854d 100644 --- a/formatters/json.go +++ b/formatters/json.go @@ -1,30 +1,44 @@ package formatters import ( + "bytes" "encoding/json" "time" "codeberg.org/danjones000/my-log/config" "codeberg.org/danjones000/my-log/models" - //"codeberg.org/danjones000/my-log/tools" ) func newJson(ff config.Formatters) (Formatter, error) { - // @todo pretty print - return &Json{ff.Json()}, nil + return &Json{ff.Json().PrettyPrint}, nil } type Json struct { - jf config.JsonFormat + prettPrint bool } func (js *Json) Name() string { return "json" } +func (js *Json) marshal(v any) (o []byte, err error) { + o, err = json.Marshal(v) + if err != nil { + return + } + if js.prettPrint { + buff := &bytes.Buffer{} + err = json.Indent(buff, o, "", "\t") + if err == nil { + o = buff.Bytes() + } + } + return +} + func (js *Json) Meta(m models.Meta) ([]byte, error) { o := map[string]any{m.Key: m.Value} - return json.Marshal(o) + return js.marshal(o) } func (js *Json) entryMap(e models.Entry) map[string]any { @@ -39,7 +53,7 @@ func (js *Json) entryMap(e models.Entry) map[string]any { } func (js *Json) Entry(e models.Entry) ([]byte, error) { - return json.Marshal(js.entryMap(e)) + return js.marshal(js.entryMap(e)) } func (js *Json) Log(l models.Log) ([]byte, error) { @@ -59,5 +73,5 @@ func (js *Json) Logs(logs []models.Log) (out []byte, err error) { } o[l.Name] = es } - return json.Marshal(o) + return js.marshal(o) } diff --git a/formatters/json_test.go b/formatters/json_test.go index a81d4ed..da6f111 100644 --- a/formatters/json_test.go +++ b/formatters/json_test.go @@ -1,16 +1,12 @@ package formatters import ( - //"bufio" - //"bytes" "fmt" "testing" "time" "codeberg.org/danjones000/my-log/models" - //"codeberg.org/danjones000/my-log/tools" "github.com/stretchr/testify/assert" - //"github.com/stretchr/testify/require" ) func TestJsonName(t *testing.T) { @@ -65,3 +61,21 @@ func TestJsonNoLogs(t *testing.T) { assert.NoError(t, err) assert.Equal(t, exp, o) } + +func TestJsonErr(t *testing.T) { + f, _ := New("json") + o, err := f.Meta(models.Meta{"foo", make(chan bool)}) + var exp []byte + assert.Error(t, err) + assert.Equal(t, exp, o) +} + +func TestJsonPretty(t *testing.T) { + f := Json{true} + o, err := f.Meta(models.Meta{"foo", 42}) + exp := `{ + "foo": 42 +}` + assert.NoError(t, err) + assert.Equal(t, exp, string(o)) +}