diff --git a/formatters/interface.go b/formatters/interface.go new file mode 100644 index 0000000..4276aed --- /dev/null +++ b/formatters/interface.go @@ -0,0 +1,10 @@ +package formatters + +import "codeberg.org/danjones000/my-log/models" + +type Formatter interface { + Name() string + 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/new.go b/formatters/new.go new file mode 100644 index 0000000..8cd567f --- /dev/null +++ b/formatters/new.go @@ -0,0 +1,34 @@ +package formatters + +import ( + "errors" + + "codeberg.org/danjones000/my-log/config" +) + +type formatMaker func(oo config.Outputs) (Formatter, error) + +var formatterMap = map[string]formatMaker{ + "plain": newPlain, +} + +func New(kind string) (f Formatter, err error) { + conf, err := config.Load() + if err != nil { + return + } + + if make, ok := formatterMap[kind]; ok { + return make(conf.Outputs) + } + + return nil, errors.New("unimplemented") +} + +func Kinds() []string { + r := []string{} + for kind, _ := range formatterMap { + r = append(r, kind) + } + return r +} diff --git a/formatters/new_test.go b/formatters/new_test.go new file mode 100644 index 0000000..bf91000 --- /dev/null +++ b/formatters/new_test.go @@ -0,0 +1,35 @@ +package formatters + +import ( + "fmt" + "os" + "testing" + + "codeberg.org/danjones000/my-log/config" + "github.com/stretchr/testify/assert" +) + +func TestKinds(t *testing.T) { + assert.Equal(t, []string{"plain"}, Kinds()) +} + +func TestNewUnsupported(t *testing.T) { + f, err := New("nope") + assert.Nil(t, f) + assert.Error(t, err) +} + +func TestNewCantGetConfig(t *testing.T) { + f, _ := os.CreateTemp("", "test") + oldConf := config.ConfigPath + config.ConfigPath = f.Name() + defer f.Close() + defer func() { + config.ConfigPath = oldConf + }() + + fmt.Fprint(f, `{"not":"toml"}`) + form, err := New("plain") + assert.Nil(t, form) + assert.Error(t, err) +} diff --git a/formatters/plain.go b/formatters/plain.go new file mode 100644 index 0000000..420b008 --- /dev/null +++ b/formatters/plain.go @@ -0,0 +1,88 @@ +package formatters + +import ( + "bytes" + + "codeberg.org/danjones000/my-log/config" + "codeberg.org/danjones000/my-log/models" + "codeberg.org/danjones000/my-log/tools" +) + +func newPlain(oo config.Outputs) (Formatter, error) { + return &PlainText{}, nil +} + +type PlainText struct { + // config might go here some day +} + +func (pt *PlainText) Name() string { + return "plain" +} + +func (pt *PlainText) Log(log models.Log) (out []byte, err error) { + if len(log.Entries) == 0 { + return + } + + buff := &bytes.Buffer{} + buff.WriteString(log.Name) + buff.WriteString("\n#######") + written := false + for _, e := range log.Entries { + bb := pt.entryBuffer(e) + if bb.Len() > 0 { + buff.WriteByte(10) + buff.WriteByte(10) + buff.ReadFrom(bb) + written = true + } + } + if written { + out = buff.Bytes() + } + return +} + +func (pt *PlainText) entryBuffer(entry models.Entry) *bytes.Buffer { + buff := &bytes.Buffer{} + buff.WriteString("Title: ") + buff.WriteString(entry.Title) + buff.WriteByte(10) + buff.WriteString("Date: ") + buff.WriteString(entry.Date.Format(tools.DateFormat)) + for _, m := range entry.Fields { + bb, err := pt.metaBuffer(m) + if (bb.Len() > 0) && (err == nil) { + buff.WriteByte(10) + buff.ReadFrom(bb) + } + } + + return buff +} + +func (pt *PlainText) Entry(entry models.Entry) ([]byte, error) { + buff := pt.entryBuffer(entry) + return buff.Bytes(), nil +} + +func (pt *PlainText) metaBuffer(meta models.Meta) (*bytes.Buffer, error) { + buff := &bytes.Buffer{} + buff.WriteString(meta.Key) + buff.WriteString(": ") + n, err := tools.WriteValue(buff, meta.Value) + if n == 0 || err != nil { + return &bytes.Buffer{}, err + } + return buff, nil +} + +func (pt *PlainText) Meta(meta models.Meta) (out []byte, err error) { + buff, err := pt.metaBuffer(meta) + if err != nil { + return + } + out = buff.Bytes() + return +} diff --git a/formatters/plain_test.go b/formatters/plain_test.go new file mode 100644 index 0000000..e384101 --- /dev/null +++ b/formatters/plain_test.go @@ -0,0 +1,109 @@ +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 TestPlainLog(t *testing.T) { + m := []models.Meta{ + {"foo", "bar"}, + {"baz", 42}, + } + e := []models.Entry{ + {Title: "one", Date: time.Now(), Fields: m}, + {Title: "small", Date: time.Now()}, + } + l := models.Log{"stuff", e} + + f, err := New("plain") + require.NoError(t, err) + + out, err := f.Log(l) + require.NoError(t, err) + + read := bytes.NewReader(out) + scan := bufio.NewScanner(read) + + scan.Scan() + line := scan.Text() + assert.Equal(t, l.Name, line) + + scan.Scan() + line = scan.Text() + assert.Equal(t, "#######", line) + + scan.Scan() + scan.Scan() + line = scan.Text() + assert.Equal(t, "Title: "+e[0].Title, line) + + scan.Scan() + line = scan.Text() + assert.Equal(t, "Date: "+e[0].Date.Format(tools.DateFormat), line) + + scan.Scan() + line = scan.Text() + assert.Equal(t, "foo: bar", line) + + scan.Scan() + line = scan.Text() + assert.Equal(t, "baz: 42", line) + + scan.Scan() + scan.Scan() + line = scan.Text() + assert.Equal(t, "Title: "+e[1].Title, line) + + scan.Scan() + line = scan.Text() + assert.Equal(t, "Date: "+e[1].Date.Format(tools.DateFormat), line) + + more := scan.Scan() + assert.False(t, more) +} + +func TestPlainName(t *testing.T) { + f, _ := New("plain") + assert.Equal(t, "plain", f.Name()) +} + +func TestPlainLogNoEntries(t *testing.T) { + f, _ := New("plain") + out, err := f.Log(models.Log{Name: "foo"}) + assert.NoError(t, err) + assert.Len(t, out, 0) +} + +func TestPlainMetaEmpty(t *testing.T) { + f, _ := New("plain") + out, err := f.Meta(models.Meta{"foo", ""}) + assert.NoError(t, err) + assert.Len(t, out, 0) +} + +func TestPlainMetaError(t *testing.T) { + f, _ := New("plain") + out, err := f.Meta(models.Meta{"foo", make(chan bool)}) + assert.Error(t, err) + assert.Len(t, out, 0) +} + +func TestPlainEntry(t *testing.T) { + f, _ := New("plain") + now := time.Now() + out, err := f.Entry(models.Entry{ + Title: "foo", + Date: now, + }) + assert.NoError(t, err) + assert.Equal(t, fmt.Sprintf("Title: foo\nDate: %s", now.Format(tools.DateFormat)), string(out)) +}