🔀 Merge branch 'feature/formatters' into develop
This commit is contained in:
		
				commit
				
					
						2fc60c16c6
					
				
			
		
					 15 changed files with 456 additions and 42 deletions
				
			
		
							
								
								
									
										11
									
								
								cmd/drop.go
									
										
									
									
									
								
							
							
						
						
									
										11
									
								
								cmd/drop.go
									
										
									
									
									
								
							|  | @ -22,6 +22,7 @@ import ( | |||
| 	"time" | ||||
| 
 | ||||
| 	"codeberg.org/danjones000/my-log/files" | ||||
| 	"codeberg.org/danjones000/my-log/formatters" | ||||
| 	"codeberg.org/danjones000/my-log/models" | ||||
| 	"codeberg.org/danjones000/my-log/tools" | ||||
| 	"github.com/spf13/cobra" | ||||
|  | @ -58,11 +59,17 @@ var dropCmd = &cobra.Command{ | |||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		by, err := e.MarshalText() | ||||
| 
 | ||||
| 		form, err := formatters.New("plain") | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		fmt.Fprintf(cmd.OutOrStdout(), "%s\n", by) | ||||
| 		out, err := form.Log(l) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		fmt.Fprintf(cmd.OutOrStdout(), "%s\n", out) | ||||
| 
 | ||||
| 		return nil | ||||
| 	}, | ||||
| } | ||||
|  |  | |||
|  | @ -27,8 +27,14 @@ dotFolder = true | |||
| [output.stdout] | ||||
| enabled = true | ||||
| [output.stdout.config] | ||||
| # Whether to output as JSON. Maybe useful to pipe elsewhere. | ||||
| json = false | ||||
| # Formatter to use when outputting to stdout | ||||
| formatter = "plain" | ||||
| 
 | ||||
| [formatters] | ||||
| 
 | ||||
| [formatters.json] | ||||
| # Set to true to pretty print JSON output | ||||
| pretty_print = false | ||||
| 
 | ||||
| ` | ||||
| 
 | ||||
|  |  | |||
|  | @ -32,6 +32,7 @@ func Load() (Config, error) { | |||
| 	} | ||||
| 	env.Parse(&c) | ||||
| 	c.Outputs["stdout"] = loadStdout(c.Outputs["stdout"]) | ||||
| 	c.Formatters["json"] = loadJsonFormat(c.Formatters["json"]) | ||||
| 
 | ||||
| 	l := "" | ||||
| 	for k, v := range Overrides { | ||||
|  | @ -77,3 +78,21 @@ func (oo Outputs) Stdout() (s Stdout, enabled bool) { | |||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func loadJsonFormat(c map[string]any) map[string]any { | ||||
| 	jf := JsonFormat{} | ||||
| 	mapst.Decode(c, &jf) | ||||
| 	env.Parse(&jf) | ||||
| 	mapst.Decode(jf, &c) | ||||
| 	return c | ||||
| } | ||||
| 
 | ||||
| func (ff Formatters) Json() (jf JsonFormat) { | ||||
| 	o, ok := ff["json"] | ||||
| 	if !ok { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	mapst.Decode(o, &jf) | ||||
| 	return | ||||
| } | ||||
|  |  | |||
|  | @ -55,7 +55,12 @@ func TestOverrideJson(t *testing.T) { | |||
| 	assert.Equal(t, "txt", c.Input.Ext) | ||||
| } | ||||
| 
 | ||||
| // @todo test time | ||||
| func TestTimeParse(t *testing.T) { | ||||
| 	Overrides = map[string]string{"input.ext": "now"} | ||||
| 	c, err := Load() | ||||
| 	assert.ErrorContains(t, err, "incompatible types: TOML value has type time.Time; destination has type string") | ||||
| 	assert.Equal(t, "txt", c.Input.Ext) | ||||
| } | ||||
| 
 | ||||
| func TestStdoutMissing(t *testing.T) { | ||||
| 	var oo Outputs = map[string]Output{} | ||||
|  | @ -65,12 +70,25 @@ func TestStdoutMissing(t *testing.T) { | |||
| } | ||||
| 
 | ||||
| func TestStdoutLoad(t *testing.T) { | ||||
| 	os.Setenv("LOG_STDOUT_JSON", "true") | ||||
| 	defer os.Unsetenv("LOG_STDOUT_JSON") | ||||
| 	os.Setenv("LOG_STDOUT_FORMATTER", "json") | ||||
| 	defer os.Unsetenv("LOG_STDOUT_FORMATTER") | ||||
| 	os.Setenv("LOG_STDOUT_ENABLED", "true") | ||||
| 	defer os.Unsetenv("LOG_STDOUT_ENABLED") | ||||
| 	c, _ := Load() | ||||
| 	std, en := c.Outputs.Stdout() | ||||
| 	assert.True(t, en) | ||||
| 	assert.True(t, std.Json) | ||||
| 	assert.Equal(t, "json", std.Formatter) | ||||
| } | ||||
| 
 | ||||
| func TestFormatJson(t *testing.T) { | ||||
| 	ff := Formatters{ | ||||
| 		"json": map[string]any{"pretty_print": true}, | ||||
| 	} | ||||
| 
 | ||||
| 	js := ff.Json() | ||||
| 	assert.True(t, js.PrettyPrint) | ||||
| 
 | ||||
| 	ff = Formatters{} | ||||
| 	js = ff.Json() | ||||
| 	assert.False(t, js.PrettyPrint) | ||||
| } | ||||
|  |  | |||
|  | @ -1,8 +1,9 @@ | |||
| package config | ||||
| 
 | ||||
| type Config struct { | ||||
| 	Input   Input | ||||
| 	Outputs Outputs `toml:"output"` | ||||
| 	Input      Input | ||||
| 	Outputs    Outputs `toml:"output"` | ||||
| 	Formatters Formatters | ||||
| } | ||||
| 
 | ||||
| type Input struct { | ||||
|  | @ -20,9 +21,15 @@ type Output struct { | |||
| } | ||||
| 
 | ||||
| type Stdout struct { | ||||
| 	Json bool `env:"LOG_STDOUT_JSON" mapstructure:"json"` | ||||
| 	Formatter string `env:"LOG_STDOUT_FORMATTER" mapstructure:"formatter"` | ||||
| } | ||||
| 
 | ||||
| type stdoutEnabled struct { | ||||
| 	Enabled bool `env:"LOG_STDOUT_ENABLED"` | ||||
| } | ||||
| 
 | ||||
| type Formatters map[string]map[string]any | ||||
| 
 | ||||
| type JsonFormat struct { | ||||
| 	PrettyPrint bool `env:"LOG_JSON_PRETTY_PRINT" mapstructure:"pretty_print"` | ||||
| } | ||||
|  |  | |||
|  | @ -57,6 +57,21 @@ func (s *AppendTestSuite) TestSuccess() { | |||
| 	s.Assert().Contains(st, "\n@bar true") | ||||
| } | ||||
| 
 | ||||
| func (s *AppendTestSuite) TestFailEntry() { | ||||
| 	e := models.Entry{ | ||||
| 		Title: "Jimmy", | ||||
| 	} | ||||
| 	l := models.Log{ | ||||
| 		Name:    "test", | ||||
| 		Entries: []models.Entry{e}, | ||||
| 	} | ||||
| 	err := Append(l) | ||||
| 	s.Assert().NoError(err) | ||||
| 	s.Require().FileExists(s.dir + "/test.log") | ||||
| 	by, _ := os.ReadFile(s.dir + "/test.log") | ||||
| 	s.Assert().Equal([]byte{}, by) | ||||
| } | ||||
| 
 | ||||
| func (s *AppendTestSuite) TestDotFolder() { | ||||
| 	config.Overrides["input.dotFolder"] = "true" | ||||
| 	e := models.Entry{ | ||||
|  |  | |||
							
								
								
									
										10
									
								
								formatters/interface.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								formatters/interface.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -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) | ||||
| } | ||||
							
								
								
									
										34
									
								
								formatters/new.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								formatters/new.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | |||
| package formatters | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 
 | ||||
| 	"codeberg.org/danjones000/my-log/config" | ||||
| ) | ||||
| 
 | ||||
| type formatMaker func(config.Formatters) (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.Formatters) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil, errors.New("unimplemented") | ||||
| } | ||||
| 
 | ||||
| func Kinds() []string { | ||||
| 	r := []string{} | ||||
| 	for kind, _ := range formatterMap { | ||||
| 		r = append(r, kind) | ||||
| 	} | ||||
| 	return r | ||||
| } | ||||
							
								
								
									
										35
									
								
								formatters/new_test.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								formatters/new_test.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -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) | ||||
| } | ||||
							
								
								
									
										88
									
								
								formatters/plain.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								formatters/plain.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -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(ff config.Formatters) (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 | ||||
| } | ||||
							
								
								
									
										109
									
								
								formatters/plain_test.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								formatters/plain_test.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -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)) | ||||
| } | ||||
|  | @ -2,12 +2,9 @@ package models | |||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"regexp" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"codeberg.org/danjones000/my-log/tools" | ||||
| ) | ||||
|  | @ -25,33 +22,9 @@ func (m Meta) MarshalText() ([]byte, error) { | |||
| 	buff.WriteRune('@') | ||||
| 	buff.WriteString(m.Key) | ||||
| 	buff.WriteRune(' ') | ||||
| 	switch v := m.Value.(type) { | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("Unknown type %T", v) | ||||
| 	case nil: | ||||
| 		return []byte{}, nil | ||||
| 	case string: | ||||
| 		buff.WriteString(v) | ||||
| 	case int: | ||||
| 		buff.WriteString(strconv.Itoa(v)) | ||||
| 	case int64: | ||||
| 		buff.WriteString(strconv.FormatInt(v, 10)) | ||||
| 	case float64: | ||||
| 		buff.WriteString(strconv.FormatFloat(v, 'f', -1, 64)) | ||||
| 	case json.Number: | ||||
| 		buff.WriteString(v.String()) | ||||
| 	case json.RawMessage: | ||||
| 		buff.Write(v) | ||||
| 	case []byte: | ||||
| 		buff.Write(v) | ||||
| 	case byte: | ||||
| 		buff.WriteByte(v) | ||||
| 	case rune: | ||||
| 		buff.WriteString(string(v)) | ||||
| 	case bool: | ||||
| 		buff.WriteString(strconv.FormatBool(v)) | ||||
| 	case time.Time: | ||||
| 		buff.WriteString(v.Format(time.RFC3339)) | ||||
| 	n, err := tools.WriteValue(buff, m.Value) | ||||
| 	if n == 0 || err != nil { | ||||
| 		return []byte{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	return buff.Bytes(), nil | ||||
|  |  | |||
|  | @ -40,7 +40,7 @@ func TestMeta(t *testing.T) { | |||
| 		{"byte", "byteme", byte(67), "@byteme C", nil, "C"}, | ||||
| 		{"json-obj", "obj", json.RawMessage(`{"foo":"bar","baz":"quux"}`), `@obj {"foo":"bar","baz":"quux"}`, nil, json.RawMessage(`{"foo":"bar","baz":"quux"}`)}, | ||||
| 		{"json-arr", "arr", json.RawMessage(`["foo",42,"bar", null,"quux", true]`), `@arr ["foo",42,"bar", null,"quux", true]`, nil, json.RawMessage(`["foo",42,"bar", null,"quux", true]`)}, | ||||
| 		{"chan", "nope", make(chan bool), "", errors.New("Unknown type chan bool"), ""}, | ||||
| 		{"chan", "nope", make(chan bool), "", errors.New("Unsupported type chan bool"), ""}, | ||||
| 		{"whitespace-key", "no space", "hi", "", errors.New("whitespace is not allowed in key: no space"), ""}, | ||||
| 		{"empty-mar", "nope", skipMarshalTest, "", nil, ErrorParsing}, | ||||
| 		{"no-key-mar", "nope", skipMarshalTest, "nope", nil, ErrorParsing}, | ||||
|  |  | |||
							
								
								
									
										44
									
								
								tools/write_buffer.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								tools/write_buffer.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,44 @@ | |||
| package tools | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| func WriteValue(buff *bytes.Buffer, val any) (n int, err error) { | ||||
| 	switch v := val.(type) { | ||||
| 	default: | ||||
| 		err = fmt.Errorf("Unsupported type %T", v) | ||||
| 	case nil: | ||||
| 		return | ||||
| 	case string: | ||||
| 		return buff.WriteString(v) | ||||
| 	case int: | ||||
| 		return buff.WriteString(strconv.Itoa(v)) | ||||
| 	case int64: | ||||
| 		return buff.WriteString(strconv.FormatInt(v, 10)) | ||||
| 	case float64: | ||||
| 		return buff.WriteString(strconv.FormatFloat(v, 'f', -1, 64)) | ||||
| 	case json.Number: | ||||
| 		return buff.WriteString(v.String()) | ||||
| 	case json.RawMessage: | ||||
| 		return buff.Write(v) | ||||
| 	case []byte: | ||||
| 		return buff.Write(v) | ||||
| 	case byte: | ||||
| 		err = buff.WriteByte(v) | ||||
| 		if err == nil { | ||||
| 			n = 1 | ||||
| 		} | ||||
| 	case rune: | ||||
| 		return buff.WriteString(string(v)) | ||||
| 	case bool: | ||||
| 		return buff.WriteString(strconv.FormatBool(v)) | ||||
| 	case time.Time: | ||||
| 		return buff.WriteString(v.Format(time.RFC3339)) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										49
									
								
								tools/write_buffer_test.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								tools/write_buffer_test.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | |||
| package tools | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
| func TestWriteBuffer(t *testing.T) { | ||||
| 	when := time.Now() | ||||
| 	tests := []struct { | ||||
| 		name  string | ||||
| 		value any | ||||
| 		out   string | ||||
| 		err   error | ||||
| 	}{ | ||||
| 		{"nil", nil, "", nil}, | ||||
| 		{"string", "hi", "hi", nil}, | ||||
| 		{"bytes", []byte{104, 105}, "hi", nil}, | ||||
| 		{"byte", byte(104), "h", nil}, | ||||
| 		{"rune", 'h', "h", nil}, | ||||
| 		{"int", 42, "42", nil}, | ||||
| 		{"int64", int64(42), "42", nil}, | ||||
| 		{"float", 42.13, "42.13", nil}, | ||||
| 		{"bool", false, "false", nil}, | ||||
| 		{"json.Number", json.Number("42.13"), "42.13", nil}, | ||||
| 		{"json.RawMessage", json.RawMessage("{}"), "{}", nil}, | ||||
| 		{"time", when, when.Format(time.RFC3339), nil}, | ||||
| 		{"struct", struct{}{}, "", errors.New("Unsupported type struct {}")}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, getWriteTestRunner(tt.value, tt.out, tt.err)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func getWriteTestRunner(value any, out string, err error) func(*testing.T) { | ||||
| 	return func(t *testing.T) { | ||||
| 		buff := &bytes.Buffer{} | ||||
| 		n, er := WriteValue(buff, value) | ||||
| 		assert.Equal(t, len(out), n) | ||||
| 		assert.Equal(t, err, er) | ||||
| 		assert.Equal(t, out, buff.String()) | ||||
| 	} | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue