♲ Refactor configuration to use viper with context propagation
- Replace global ConfigPath and Overrides with viper-based configuration - Add viper.New() to create configurable viper instances - Store viper and unmarshaled Config struct in context for testability - Add RetrieveFromContext and AddToContext helper functions - Update files.Append to accept context and retrieve config from it - Update formatters.Preferred and formatters.New to accept context - Add PersistentPreRunE in CLI to create and configure viper instance - Support -c flag for custom config file path - Support -v flag for config value overrides - Update all test files to create viper and add to context - Remove unused config types and load functions - Add viper as dependency with automatic env var support (MYLOG_*)
This commit is contained in:
parent
d34363b8c0
commit
9f05f933dd
21 changed files with 338 additions and 360 deletions
|
|
@ -32,23 +32,24 @@ var ConfigCmd = &cobra.Command{
|
|||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
print, _ := cmd.Flags().GetBool("print")
|
||||
if print {
|
||||
fmt.Fprintln(cmd.OutOrStdout(), config.ConfigPath)
|
||||
fmt.Fprintln(cmd.OutOrStdout(), config.DefaultPath())
|
||||
return nil
|
||||
}
|
||||
force, _ := cmd.Flags().GetBool("force")
|
||||
configPath := config.DefaultPath()
|
||||
if !force {
|
||||
_, err = os.Stat(config.ConfigPath)
|
||||
_, err = os.Stat(configPath)
|
||||
if !os.IsNotExist(err) {
|
||||
return fmt.Errorf("%s already exists. Use -f to overwrite", config.ConfigPath)
|
||||
return fmt.Errorf("%s already exists. Use -f to overwrite", configPath)
|
||||
}
|
||||
}
|
||||
dir := fp.Dir(config.ConfigPath)
|
||||
dir := fp.Dir(configPath)
|
||||
err = os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
f, err := os.Create(config.ConfigPath)
|
||||
f, err := os.Create(configPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,8 +42,9 @@ var DropCmd = &cobra.Command{
|
|||
Args: cobra.ExactArgs(2),
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
v, _ := config.RetrieveFromContext(cmd.Context())
|
||||
if outJson {
|
||||
config.Overrides["output.stdout.config.format"] = "json"
|
||||
v.Set("output.stdout.config.format", "json")
|
||||
}
|
||||
|
||||
log := args[0]
|
||||
|
|
@ -60,12 +61,12 @@ var DropCmd = &cobra.Command{
|
|||
}
|
||||
e := models.Entry{Title: title, Date: d.Time(), Fields: *ms}
|
||||
l := models.Log{Name: log, Entries: []models.Entry{e}}
|
||||
err := files.Append(l)
|
||||
err := files.Append(cmd.Context(), l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
form, err := formatters.Preferred()
|
||||
form, err := formatters.Preferred(cmd.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
36
cli/root.go
36
cli/root.go
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright © 2024 Dan Jones <danjones@goodevilgenius.org>
|
||||
Copyright © 2026 Dan Jones <danjones@goodevilgenius.org>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
|
|
@ -17,6 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"codeberg.org/danjones000/my-log/config"
|
||||
|
|
@ -26,16 +27,41 @@ import (
|
|||
var RootCmd = &cobra.Command{
|
||||
Use: "my-log",
|
||||
Short: "A brief description of your application",
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
ctx, v, err := config.New(cmd.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if configPath != "" {
|
||||
v.SetConfigFile(configPath)
|
||||
err := v.ReadInConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for k, val := range configValues {
|
||||
v.Set(k, val)
|
||||
}
|
||||
|
||||
cmd.SetContext(ctx)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
err := RootCmd.Execute()
|
||||
func Execute(ctx context.Context) {
|
||||
err := RootCmd.ExecuteContext(ctx)
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
var configPath string
|
||||
var configValues map[string]string
|
||||
|
||||
func init() {
|
||||
RootCmd.PersistentFlags().StringVarP(&config.ConfigPath, "config", "c", config.ConfigPath, "config file")
|
||||
RootCmd.PersistentFlags().StringToStringVarP(&config.Overrides, "config-value", "v", config.Overrides, "Override config values. Use dot syntax to specify key. E.g. -v output.stdout.config.format=json")
|
||||
RootCmd.PersistentFlags().StringVarP(&configPath, "config", "c", config.DefaultPath(), "config file")
|
||||
RootCmd.PersistentFlags().StringToStringVarP(&configValues, "config-value", "v", nil, "Override config values. Use dot syntax to specify key. E.g. -v output.stdout.config.format=json")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright © 2024 Dan Jones <danjones@goodevilgenius.org>
|
||||
Copyright © 2026 Dan Jones <danjones@goodevilgenius.org>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
|
|
@ -16,8 +16,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/
|
||||
package main
|
||||
|
||||
import "codeberg.org/danjones000/my-log/cli"
|
||||
import (
|
||||
"context"
|
||||
|
||||
"codeberg.org/danjones000/my-log/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cli.Execute()
|
||||
cli.Execute(context.Background())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
fp "path/filepath"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
const ConfigStr = `# Configuration for my-log
|
||||
|
|
@ -38,15 +36,13 @@ pretty_print = false
|
|||
|
||||
`
|
||||
|
||||
func DefaultPath() string {
|
||||
conf, _ := os.UserConfigDir()
|
||||
return fp.Join(conf, "my-log", "config.toml")
|
||||
}
|
||||
|
||||
func DefaultStr() string {
|
||||
home, _ := os.UserHomeDir()
|
||||
inDir := fp.Join(home, "my-log")
|
||||
return fmt.Sprintf(ConfigStr, inDir)
|
||||
}
|
||||
|
||||
func DefaultConfig() (Config, error) {
|
||||
s := DefaultStr()
|
||||
c := Config{}
|
||||
_, err := toml.Decode(s, &c)
|
||||
return c, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
fp "path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/nalgeon/be"
|
||||
)
|
||||
|
||||
func TestDefaultConfig(t *testing.T) {
|
||||
home, _ := os.UserHomeDir()
|
||||
inDir := fp.Join(home, "my-log")
|
||||
c, err := DefaultConfig()
|
||||
be.Err(t, err, nil)
|
||||
be.Equal(t, c.Input.Path, inDir)
|
||||
}
|
||||
119
config/load.go
119
config/load.go
|
|
@ -1,98 +1,53 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
fp "path/filepath"
|
||||
"time"
|
||||
"strings"
|
||||
|
||||
"codeberg.org/danjones000/my-log/tools"
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/caarlos0/env/v10"
|
||||
mapst "github.com/go-viper/mapstructure/v2"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var ConfigPath string
|
||||
var Overrides = map[string]string{}
|
||||
type confKeyType uint8
|
||||
|
||||
func init() {
|
||||
conf, _ := os.UserConfigDir()
|
||||
ConfigPath = fp.Join(conf, "my-log", "config.toml")
|
||||
}
|
||||
const (
|
||||
_ confKeyType = iota
|
||||
viperKey
|
||||
)
|
||||
|
||||
func Load() (Config, error) {
|
||||
c, _ := DefaultConfig()
|
||||
_, err := os.Stat(ConfigPath)
|
||||
if !os.IsNotExist(err) {
|
||||
_, err = toml.DecodeFile(ConfigPath, &c)
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
}
|
||||
env.Parse(&c)
|
||||
c.Outputs["stdout"] = loadStdout(c.Outputs["stdout"])
|
||||
c.Formatters["json"] = loadJsonFormat(c.Formatters["json"])
|
||||
|
||||
l := ""
|
||||
for k, v := range Overrides {
|
||||
val := tools.ParseString(v)
|
||||
if val == nil {
|
||||
continue
|
||||
}
|
||||
if _, isJson := val.(json.RawMessage); isJson {
|
||||
continue
|
||||
}
|
||||
valout := fmt.Sprintf("%v", val)
|
||||
if vals, isString := val.(string); isString {
|
||||
valout = fmt.Sprintf(`"%s"`, vals)
|
||||
}
|
||||
if valt, isTime := val.(time.Time); isTime {
|
||||
valout = valt.Format(time.RFC3339)
|
||||
}
|
||||
l = l + "\n" + fmt.Sprintf("%s = %s", k, valout)
|
||||
}
|
||||
_, err = toml.Decode(l, &c)
|
||||
return c, err
|
||||
}
|
||||
|
||||
func loadStdout(stdout Output) Output {
|
||||
st := stdoutEnabled{stdout.Enabled}
|
||||
env.Parse(&st)
|
||||
stdout.Enabled = st.Enabled
|
||||
var std Stdout
|
||||
mapst.Decode(stdout.Config, &std)
|
||||
env.Parse(&std)
|
||||
mapst.Decode(std, &stdout.Config)
|
||||
return stdout
|
||||
}
|
||||
|
||||
func (oo Outputs) Stdout() (s Stdout, enabled bool) {
|
||||
o, ok := oo["stdout"]
|
||||
func RetrieveFromContext(ctx context.Context) (*viper.Viper, Config) {
|
||||
v, ok := ctx.Value(viperKey).(*viper.Viper)
|
||||
if !ok {
|
||||
return s, false
|
||||
panic("config not found in context")
|
||||
}
|
||||
var c Config
|
||||
if err := v.Unmarshal(&c); err != nil {
|
||||
panic(fmt.Errorf("failed to unmarshal config: %w", err))
|
||||
}
|
||||
return v, c
|
||||
}
|
||||
|
||||
func AddToContext(ctx context.Context, v *viper.Viper) context.Context {
|
||||
return context.WithValue(ctx, viperKey, v)
|
||||
}
|
||||
|
||||
func New(ctx context.Context) (context.Context, *viper.Viper, error) {
|
||||
v := viper.New()
|
||||
v.SetConfigType("toml")
|
||||
|
||||
if err := v.ReadConfig(bytes.NewBufferString(DefaultStr())); err != nil {
|
||||
return ctx, nil, err
|
||||
}
|
||||
|
||||
enabled = o.Enabled
|
||||
mapst.Decode(o.Config, &s)
|
||||
v.SetConfigFile(DefaultPath())
|
||||
v.SetEnvPrefix("MYLOG")
|
||||
v.AutomaticEnv()
|
||||
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
|
||||
|
||||
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
|
||||
if err := v.ReadInConfig(); err != nil {
|
||||
return ctx, nil, err
|
||||
}
|
||||
|
||||
mapst.Decode(o, &jf)
|
||||
return
|
||||
return AddToContext(ctx, v), v, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,93 +1,51 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/nalgeon/be"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestLoad(t *testing.T) {
|
||||
f, _ := os.CreateTemp("", "test")
|
||||
ConfigPath = f.Name()
|
||||
defer f.Close()
|
||||
fmt.Fprint(f, `[input]
|
||||
func TestNew(t *testing.T) {
|
||||
_, v, err := New(t.Context())
|
||||
be.Err(t, err, nil)
|
||||
be.True(t, v != nil)
|
||||
}
|
||||
|
||||
func TestNewWithEnvOverrides(t *testing.T) {
|
||||
os.Setenv("MYLOG_INPUT_PATH", "/test/path")
|
||||
defer os.Unsetenv("MYLOG_INPUT_PATH")
|
||||
|
||||
_, v, err := New(t.Context())
|
||||
be.Err(t, err, nil)
|
||||
be.Equal(t, v.GetString("input.path"), "/test/path")
|
||||
}
|
||||
|
||||
func TestNewWithConfigFile(t *testing.T) {
|
||||
dir := t.ArtifactDir()
|
||||
f, _ := os.CreateTemp(dir, "test*.toml")
|
||||
defer os.Remove(f.Name())
|
||||
f.WriteString(`[input]
|
||||
path = "/file/path"
|
||||
ext = "log"`)
|
||||
c, err := Load()
|
||||
f.Close()
|
||||
|
||||
_, v, err := New(t.Context())
|
||||
be.Err(t, err, nil)
|
||||
be.Equal(t, c.Input.Ext, "log")
|
||||
}
|
||||
|
||||
func TestLoadBadFile(t *testing.T) {
|
||||
f, _ := os.CreateTemp("", "test")
|
||||
ConfigPath = f.Name()
|
||||
defer f.Close()
|
||||
fmt.Fprint(f, `{"not":"toml"}`)
|
||||
_, err := Load()
|
||||
be.Err(t, err)
|
||||
}
|
||||
|
||||
func TestLoadIgnoreMissingFile(t *testing.T) {
|
||||
def, _ := DefaultConfig()
|
||||
ConfigPath = "/not/a/real/file"
|
||||
c, err := Load()
|
||||
v.SetConfigFile(f.Name())
|
||||
v.SetConfigType("toml")
|
||||
err = v.ReadInConfig()
|
||||
be.Err(t, err, nil)
|
||||
be.Equal(t, c, def)
|
||||
be.Equal(t, v.GetString("input.path"), "/file/path")
|
||||
be.Equal(t, v.GetString("input.ext"), "log")
|
||||
}
|
||||
|
||||
func TestOverride(t *testing.T) {
|
||||
Overrides = map[string]string{
|
||||
"input.path": "/path/to/it",
|
||||
"input.ext": "~",
|
||||
}
|
||||
c, err := Load()
|
||||
be.Err(t, err, nil)
|
||||
be.Equal(t, c.Input.Path, Overrides["input.path"])
|
||||
be.Equal(t, c.Input.Ext, "txt")
|
||||
}
|
||||
|
||||
func TestOverrideJson(t *testing.T) {
|
||||
Overrides = map[string]string{"input.ext": `{"a":"b"}`}
|
||||
c, err := Load()
|
||||
be.Err(t, err, nil)
|
||||
be.Equal(t, c.Input.Ext, "txt")
|
||||
}
|
||||
|
||||
func TestTimeParse(t *testing.T) {
|
||||
Overrides = map[string]string{"input.ext": "now"}
|
||||
c, err := Load()
|
||||
be.Err(t, err, "incompatible types: TOML value has type time.Time; destination has type string")
|
||||
be.Equal(t, c.Input.Ext, "txt")
|
||||
}
|
||||
|
||||
func TestStdoutMissing(t *testing.T) {
|
||||
var oo Outputs = map[string]Output{}
|
||||
std, en := oo.Stdout()
|
||||
be.True(t, !en)
|
||||
be.Equal(t, std, Stdout{})
|
||||
}
|
||||
|
||||
func TestStdoutLoad(t *testing.T) {
|
||||
os.Setenv("LOG_STDOUT_FORMAT", "json")
|
||||
defer os.Unsetenv("LOG_STDOUT_FORMAT")
|
||||
os.Setenv("LOG_STDOUT_ENABLED", "true")
|
||||
defer os.Unsetenv("LOG_STDOUT_ENABLED")
|
||||
c, _ := Load()
|
||||
std, en := c.Outputs.Stdout()
|
||||
be.True(t, en)
|
||||
be.Equal(t, std.Format, "json")
|
||||
}
|
||||
|
||||
func TestFormatJson(t *testing.T) {
|
||||
ff := Formatters{
|
||||
"json": map[string]any{"pretty_print": true},
|
||||
}
|
||||
|
||||
js := ff.Json()
|
||||
be.True(t, js.PrettyPrint)
|
||||
|
||||
ff = Formatters{}
|
||||
js = ff.Json()
|
||||
be.True(t, !js.PrettyPrint)
|
||||
func TestRetrieveFromContext(t *testing.T) {
|
||||
v := viper.New()
|
||||
ctx := AddToContext(t.Context(), v)
|
||||
result, _ := RetrieveFromContext(ctx)
|
||||
be.True(t, v == result)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,15 +2,15 @@ package config
|
|||
|
||||
type Config struct {
|
||||
Input Input
|
||||
Outputs Outputs `toml:"output"`
|
||||
Outputs Outputs `mapstructure:"output"`
|
||||
Formatters Formatters
|
||||
}
|
||||
|
||||
type Input struct {
|
||||
Path string `env:"LOG_PATH"`
|
||||
Recurse bool `env:"LOG_RECURSE"`
|
||||
Ext string `env:"LOG_EXT"`
|
||||
DotFolder bool `env:"LOG_DOT_FOLDER"`
|
||||
Path string
|
||||
Recurse bool
|
||||
Ext string
|
||||
DotFolder bool `mapstructure:"dotFolder"`
|
||||
}
|
||||
|
||||
type Outputs map[string]Output
|
||||
|
|
@ -20,16 +20,4 @@ type Output struct {
|
|||
Config map[string]any
|
||||
}
|
||||
|
||||
type Stdout struct {
|
||||
Format string `env:"LOG_STDOUT_FORMAT" mapstructure:"format"`
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package files
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
|
@ -11,11 +12,8 @@ import (
|
|||
"codeberg.org/danjones000/my-log/models"
|
||||
)
|
||||
|
||||
func Append(l models.Log) error {
|
||||
conf, err := config.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func Append(ctx context.Context, l models.Log) error {
|
||||
_, conf := config.RetrieveFromContext(ctx)
|
||||
|
||||
filename := l.Name
|
||||
if conf.Input.DotFolder {
|
||||
|
|
@ -27,7 +25,7 @@ func Append(l models.Log) error {
|
|||
}
|
||||
path := fp.Join(conf.Input.Path, filename)
|
||||
dir := fp.Dir(path)
|
||||
err = os.MkdirAll(dir, 0750)
|
||||
err := os.MkdirAll(dir, 0750)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package files
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
|
@ -10,6 +11,7 @@ import (
|
|||
"codeberg.org/danjones000/my-log/config"
|
||||
"codeberg.org/danjones000/my-log/models"
|
||||
"github.com/nalgeon/be"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestAppend(tt *testing.T) {
|
||||
|
|
@ -24,26 +26,25 @@ func TestAppend(tt *testing.T) {
|
|||
})
|
||||
tt.Run("failure", func(t *testing.T) {
|
||||
t.Run("badEntry", appendTestBadEntry)
|
||||
t.Run("load-err", appendTestConfLoadErr)
|
||||
t.Run("mkdir-err", appendTestMkdirErr)
|
||||
t.Run("append-log-err", appendTestOpenErr)
|
||||
})
|
||||
}
|
||||
|
||||
func setupAppendTest(t *testing.T) string {
|
||||
func setupAppendTest(t *testing.T) (string, context.Context) {
|
||||
t.Helper()
|
||||
dir := t.ArtifactDir()
|
||||
config.Overrides["input.path"] = dir
|
||||
config.Overrides["input.ext"] = "log"
|
||||
t.Cleanup(func() {
|
||||
delete(config.Overrides, "input.path")
|
||||
delete(config.Overrides, "input.ext")
|
||||
})
|
||||
return dir
|
||||
v := viper.New()
|
||||
v.SetConfigType("toml")
|
||||
v.Set("input.path", dir)
|
||||
v.Set("input.ext", "log")
|
||||
v.Set("input.dotFolder", false)
|
||||
ctx := config.AddToContext(t.Context(), v)
|
||||
return dir, ctx
|
||||
}
|
||||
|
||||
func appendTestSingle(t *testing.T) {
|
||||
dir := setupAppendTest(t)
|
||||
dir, ctx := setupAppendTest(t)
|
||||
when := time.Now().Local()
|
||||
e := models.Entry{
|
||||
Title: "Jimmy",
|
||||
|
|
@ -57,7 +58,7 @@ func appendTestSingle(t *testing.T) {
|
|||
Name: "test",
|
||||
Entries: []models.Entry{e},
|
||||
}
|
||||
err := Append(l)
|
||||
err := Append(ctx, l)
|
||||
be.Err(t, err, nil)
|
||||
by, err := os.ReadFile(dir + "/test.log")
|
||||
be.Err(t, err, nil)
|
||||
|
|
@ -68,7 +69,7 @@ func appendTestSingle(t *testing.T) {
|
|||
}
|
||||
|
||||
func appendTestTwoEntries(t *testing.T) {
|
||||
dir := setupAppendTest(t)
|
||||
dir, ctx := setupAppendTest(t)
|
||||
when := time.Now().Local()
|
||||
whens := when.Format(models.DateFormat)
|
||||
e := []models.Entry{
|
||||
|
|
@ -79,7 +80,7 @@ func appendTestTwoEntries(t *testing.T) {
|
|||
Name: "test",
|
||||
Entries: e,
|
||||
}
|
||||
err := Append(l)
|
||||
err := Append(ctx, l)
|
||||
be.Err(t, err, nil)
|
||||
by, _ := os.ReadFile(dir + "/test.log")
|
||||
st := string(by)
|
||||
|
|
@ -88,7 +89,7 @@ func appendTestTwoEntries(t *testing.T) {
|
|||
}
|
||||
|
||||
func appendTestAddNewLine(t *testing.T) {
|
||||
dir := setupAppendTest(t)
|
||||
dir, ctx := setupAppendTest(t)
|
||||
os.WriteFile(dir+"/test.log", []byte("foo"), 0644)
|
||||
when := time.Now().Local()
|
||||
whens := when.Format(models.DateFormat)
|
||||
|
|
@ -99,7 +100,7 @@ func appendTestAddNewLine(t *testing.T) {
|
|||
Name: "test",
|
||||
Entries: e,
|
||||
}
|
||||
err := Append(l)
|
||||
err := Append(ctx, l)
|
||||
be.Err(t, err, nil)
|
||||
by, _ := os.ReadFile(dir + "/test.log")
|
||||
exp := fmt.Sprintf("foo\n@begin %s - one\n@id jimmy @end\n", whens)
|
||||
|
|
@ -107,7 +108,7 @@ func appendTestAddNewLine(t *testing.T) {
|
|||
}
|
||||
|
||||
func appendTestDontAddNewLine(t *testing.T) {
|
||||
dir := setupAppendTest(t)
|
||||
dir, ctx := setupAppendTest(t)
|
||||
os.WriteFile(dir+"/test.log", []byte("foo\n"), 0644)
|
||||
when := time.Now().Local()
|
||||
whens := when.Format(models.DateFormat)
|
||||
|
|
@ -118,7 +119,7 @@ func appendTestDontAddNewLine(t *testing.T) {
|
|||
Name: "test",
|
||||
Entries: e,
|
||||
}
|
||||
err := Append(l)
|
||||
err := Append(ctx, l)
|
||||
be.Err(t, err, nil)
|
||||
by, _ := os.ReadFile(dir + "/test.log")
|
||||
exp := fmt.Sprintf("foo\n@begin %s - one\n@id jimmy @end\n", whens)
|
||||
|
|
@ -126,7 +127,7 @@ func appendTestDontAddNewLine(t *testing.T) {
|
|||
}
|
||||
|
||||
func appendTestBadEntry(t *testing.T) {
|
||||
dir := setupAppendTest(t)
|
||||
dir, ctx := setupAppendTest(t)
|
||||
e := models.Entry{
|
||||
Title: "Jimmy",
|
||||
}
|
||||
|
|
@ -134,19 +135,21 @@ func appendTestBadEntry(t *testing.T) {
|
|||
Name: "test",
|
||||
Entries: []models.Entry{e},
|
||||
}
|
||||
err := Append(l)
|
||||
err := Append(ctx, l)
|
||||
be.Err(t, err, nil)
|
||||
by, _ := os.ReadFile(dir + "/test.log")
|
||||
be.Equal(t, by, []byte{})
|
||||
}
|
||||
|
||||
func appendTestDotFolder(t *testing.T) {
|
||||
config.Overrides["input.dotFolder"] = "true"
|
||||
t.Cleanup(func() {
|
||||
delete(config.Overrides, "input.dotFolder")
|
||||
})
|
||||
dir := t.ArtifactDir()
|
||||
v := viper.New()
|
||||
v.SetConfigType("toml")
|
||||
v.Set("input.path", dir)
|
||||
v.Set("input.ext", "log")
|
||||
v.Set("input.dotFolder", true)
|
||||
ctx := config.AddToContext(t.Context(), v)
|
||||
|
||||
dir := setupAppendTest(t)
|
||||
e := models.Entry{
|
||||
Title: "something",
|
||||
Date: time.Now(),
|
||||
|
|
@ -155,7 +158,7 @@ func appendTestDotFolder(t *testing.T) {
|
|||
Name: "sub.test",
|
||||
Entries: []models.Entry{e},
|
||||
}
|
||||
err := Append(l)
|
||||
err := Append(ctx, l)
|
||||
be.Err(t, err, nil)
|
||||
by, err := os.ReadFile(dir + "/sub/test.log")
|
||||
be.Err(t, err, nil)
|
||||
|
|
@ -164,12 +167,7 @@ func appendTestDotFolder(t *testing.T) {
|
|||
}
|
||||
|
||||
func appendTestDotFolderNo(t *testing.T) {
|
||||
config.Overrides["input.dotFolder"] = "false"
|
||||
t.Cleanup(func() {
|
||||
delete(config.Overrides, "input.dotFolder")
|
||||
})
|
||||
|
||||
dir := setupAppendTest(t)
|
||||
dir, ctx := setupAppendTest(t)
|
||||
e := models.Entry{
|
||||
Title: "another",
|
||||
Date: time.Now(),
|
||||
|
|
@ -178,7 +176,7 @@ func appendTestDotFolderNo(t *testing.T) {
|
|||
Name: "sub.test",
|
||||
Entries: []models.Entry{e},
|
||||
}
|
||||
err := Append(l)
|
||||
err := Append(ctx, l)
|
||||
be.Err(t, err, nil)
|
||||
by, err := os.ReadFile(dir + "/sub.test.log")
|
||||
be.Err(t, err, nil)
|
||||
|
|
@ -187,11 +185,13 @@ func appendTestDotFolderNo(t *testing.T) {
|
|||
}
|
||||
|
||||
func appendTestNoExt(t *testing.T) {
|
||||
dir := setupAppendTest(t)
|
||||
config.Overrides["input.ext"] = ""
|
||||
t.Cleanup(func() {
|
||||
config.Overrides["input.ext"] = "log"
|
||||
})
|
||||
dir := t.ArtifactDir()
|
||||
v := viper.New()
|
||||
v.SetConfigType("toml")
|
||||
v.Set("input.path", dir)
|
||||
v.Set("input.ext", "")
|
||||
v.Set("input.dotFolder", false)
|
||||
ctx := config.AddToContext(t.Context(), v)
|
||||
|
||||
e := models.Entry{
|
||||
Title: "baz",
|
||||
|
|
@ -201,7 +201,7 @@ func appendTestNoExt(t *testing.T) {
|
|||
Name: "foobar",
|
||||
Entries: []models.Entry{e},
|
||||
}
|
||||
err := Append(l)
|
||||
err := Append(ctx, l)
|
||||
be.Err(t, err, nil)
|
||||
by, err := os.ReadFile(dir + "/foobar")
|
||||
be.Err(t, err, nil)
|
||||
|
|
@ -209,33 +209,20 @@ func appendTestNoExt(t *testing.T) {
|
|||
be.True(t, strings.Contains(st, fmt.Sprintf("@begin %s - %s", e.Date.Format(models.DateFormat), e.Title)))
|
||||
}
|
||||
|
||||
func appendTestConfLoadErr(t *testing.T) {
|
||||
dir := t.ArtifactDir()
|
||||
|
||||
currConf := config.ConfigPath
|
||||
tmp, _ := os.CreateTemp(dir, "app-conf-*.toml")
|
||||
fname := tmp.Name()
|
||||
t.Cleanup(func() { tmp.Close() })
|
||||
t.Cleanup(func() { os.Remove(fname) })
|
||||
fmt.Fprintln(tmp, `{"not":"toml"}`)
|
||||
config.ConfigPath = fname
|
||||
t.Cleanup(func() { config.ConfigPath = currConf })
|
||||
|
||||
err := Append(models.Log{})
|
||||
be.Err(t, err, "toml")
|
||||
}
|
||||
|
||||
func appendTestMkdirErr(t *testing.T) {
|
||||
// Don't run this test as root
|
||||
config.Overrides["input.path"] = "/var/my-logs-test"
|
||||
t.Cleanup(func() { delete(config.Overrides, "input.path") })
|
||||
v := viper.New()
|
||||
v.SetConfigType("toml")
|
||||
v.Set("input.path", "/var/my-logs-test")
|
||||
v.Set("input.ext", "log")
|
||||
v.Set("input.dotFolder", false)
|
||||
ctx := config.AddToContext(t.Context(), v)
|
||||
|
||||
err := Append(models.Log{})
|
||||
err := Append(ctx, models.Log{})
|
||||
be.Err(t, err, "permission denied")
|
||||
}
|
||||
|
||||
func appendTestOpenErr(t *testing.T) {
|
||||
dir := setupAppendTest(t)
|
||||
dir, ctx := setupAppendTest(t)
|
||||
l := models.Log{
|
||||
Name: "test-open-err",
|
||||
}
|
||||
|
|
@ -244,6 +231,6 @@ func appendTestOpenErr(t *testing.T) {
|
|||
f, _ := os.Create(fname)
|
||||
f.Close()
|
||||
os.Chmod(fname, 0400)
|
||||
err := Append(l)
|
||||
err := Append(ctx, l)
|
||||
be.Err(t, err, "permission denied")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,17 @@ import (
|
|||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"codeberg.org/danjones000/my-log/config"
|
||||
"codeberg.org/danjones000/my-log/models"
|
||||
)
|
||||
|
||||
func newJson(ff config.Formatters) (Formatter, error) {
|
||||
return &Json{ff.Json().PrettyPrint}, nil
|
||||
func newJson(ff map[string]any) (Formatter, error) {
|
||||
prettyPrint := false
|
||||
if jf, ok := ff["json"].(map[string]any); ok {
|
||||
if pp, ok := jf["pretty_print"].(bool); ok {
|
||||
prettyPrint = pp
|
||||
}
|
||||
}
|
||||
return &Json{prettyPrint}, nil
|
||||
}
|
||||
|
||||
type Json struct {
|
||||
|
|
|
|||
|
|
@ -1,22 +1,35 @@
|
|||
package formatters
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"codeberg.org/danjones000/my-log/config"
|
||||
"codeberg.org/danjones000/my-log/internal/testutil/bep"
|
||||
"codeberg.org/danjones000/my-log/models"
|
||||
"github.com/nalgeon/be"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func setupJsonTestContext(t *testing.T) context.Context {
|
||||
t.Helper()
|
||||
v := viper.New()
|
||||
v.SetConfigType("toml")
|
||||
v.Set("formatters.json.pretty_print", false)
|
||||
return config.AddToContext(t.Context(), v)
|
||||
}
|
||||
|
||||
func TestJsonName(t *testing.T) {
|
||||
f, _ := New("json")
|
||||
ctx := setupJsonTestContext(t)
|
||||
f, _ := New(ctx, "json")
|
||||
be.Equal(t, f.Name(), "json")
|
||||
}
|
||||
|
||||
func TestJsonMeta(t *testing.T) {
|
||||
f, _ := New("json")
|
||||
ctx := setupJsonTestContext(t)
|
||||
f, _ := New(ctx, "json")
|
||||
m := models.Meta{Key: "foo", Value: 42}
|
||||
exp := `{"foo":42}`
|
||||
o, err := f.Meta(m)
|
||||
|
|
@ -25,8 +38,9 @@ func TestJsonMeta(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestJsonEntry(t *testing.T) {
|
||||
ctx := setupJsonTestContext(t)
|
||||
when := time.Now()
|
||||
f, _ := New("json")
|
||||
f, _ := New(ctx, "json")
|
||||
m := models.Meta{Key: "foo", Value: 42}
|
||||
e := models.Entry{
|
||||
Title: "Homer",
|
||||
|
|
@ -40,8 +54,9 @@ func TestJsonEntry(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestJsonLog(t *testing.T) {
|
||||
ctx := setupJsonTestContext(t)
|
||||
when := time.Now()
|
||||
f, _ := New("json")
|
||||
f, _ := New(ctx, "json")
|
||||
m := models.Meta{Key: "foo", Value: 42}
|
||||
e := models.Entry{
|
||||
Title: "Homer",
|
||||
|
|
@ -56,7 +71,8 @@ func TestJsonLog(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestJsonNoLogs(t *testing.T) {
|
||||
f, _ := New("json")
|
||||
ctx := setupJsonTestContext(t)
|
||||
f, _ := New(ctx, "json")
|
||||
o, err := f.Logs([]models.Log{})
|
||||
var exp []byte
|
||||
be.Err(t, err, nil)
|
||||
|
|
@ -64,7 +80,8 @@ func TestJsonNoLogs(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestJsonErr(t *testing.T) {
|
||||
f, _ := New("json")
|
||||
ctx := setupJsonTestContext(t)
|
||||
f, _ := New(ctx, "json")
|
||||
o, err := f.Meta(models.Meta{Key: "foo", Value: make(chan bool)})
|
||||
var exp []byte
|
||||
be.Err(t, err)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
package formatters
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"codeberg.org/danjones000/my-log/config"
|
||||
)
|
||||
|
||||
type formatMaker func(config.Formatters) (Formatter, error)
|
||||
type formatMaker func(config map[string]any) (Formatter, error)
|
||||
|
||||
var formatterMap = map[string]formatMaker{
|
||||
"plain": newPlain,
|
||||
|
|
@ -14,23 +15,22 @@ var formatterMap = map[string]formatMaker{
|
|||
"zero": newNull,
|
||||
}
|
||||
|
||||
func Preferred() (f Formatter, err error) {
|
||||
conf, err := config.Load()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
std, _ := conf.Outputs.Stdout()
|
||||
return New(std.Format)
|
||||
func Preferred(ctx context.Context) (f Formatter, err error) {
|
||||
v, _ := config.RetrieveFromContext(ctx)
|
||||
format := v.GetString("output.stdout.config.format")
|
||||
return New(ctx, format)
|
||||
}
|
||||
|
||||
func New(kind string) (f Formatter, err error) {
|
||||
conf, err := config.Load()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
func New(ctx context.Context, kind string) (f Formatter, err error) {
|
||||
_, c := config.RetrieveFromContext(ctx)
|
||||
conf := c.Formatters
|
||||
|
||||
if make, ok := formatterMap[kind]; ok {
|
||||
return make(conf.Formatters)
|
||||
var formatterConf map[string]any
|
||||
if cf, ok := conf[kind]; ok {
|
||||
formatterConf = cf
|
||||
}
|
||||
return make(formatterConf)
|
||||
}
|
||||
|
||||
return nil, errors.New("unimplemented")
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
package formatters
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"context"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"codeberg.org/danjones000/my-log/config"
|
||||
"github.com/nalgeon/be"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestKinds(t *testing.T) {
|
||||
|
|
@ -17,33 +17,25 @@ func TestKinds(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func setupNewTest(t *testing.T) context.Context {
|
||||
t.Helper()
|
||||
v := viper.New()
|
||||
v.SetConfigType("toml")
|
||||
v.Set("output.stdout.config.format", "plain")
|
||||
v.Set("formatters.json.pretty_print", false)
|
||||
return config.AddToContext(t.Context(), v)
|
||||
}
|
||||
|
||||
func TestNewUnsupported(t *testing.T) {
|
||||
f, err := New("nope")
|
||||
ctx := setupNewTest(t)
|
||||
f, err := New(ctx, "nope")
|
||||
be.Equal(t, f, nil)
|
||||
be.Err(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")
|
||||
be.Equal(t, form, nil)
|
||||
be.Err(t, err)
|
||||
|
||||
form, err = Preferred()
|
||||
be.Equal(t, form, nil)
|
||||
be.Err(t, err)
|
||||
}
|
||||
|
||||
func TestPreferred(t *testing.T) {
|
||||
form, err := Preferred()
|
||||
ctx := setupNewTest(t)
|
||||
form, err := Preferred(ctx)
|
||||
be.Err(t, err, nil)
|
||||
be.True(t, form != nil)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
package formatters
|
||||
|
||||
import (
|
||||
"codeberg.org/danjones000/my-log/config"
|
||||
"codeberg.org/danjones000/my-log/models"
|
||||
)
|
||||
|
||||
func newNull(ff config.Formatters) (Formatter, error) {
|
||||
func newNull(ff map[string]any) (Formatter, error) {
|
||||
return &Null{}, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,44 +1,59 @@
|
|||
package formatters
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"codeberg.org/danjones000/my-log/config"
|
||||
"codeberg.org/danjones000/my-log/models"
|
||||
"github.com/nalgeon/be"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var empty []byte
|
||||
|
||||
func setupNullTestContext(t *testing.T) context.Context {
|
||||
t.Helper()
|
||||
v := viper.New()
|
||||
v.SetConfigType("toml")
|
||||
return config.AddToContext(t.Context(), v)
|
||||
}
|
||||
|
||||
func TestNullName(t *testing.T) {
|
||||
f, err := New("zero")
|
||||
ctx := setupNullTestContext(t)
|
||||
f, err := New(ctx, "zero")
|
||||
be.Err(t, err, nil)
|
||||
be.Equal(t, f.Name(), "zero")
|
||||
}
|
||||
|
||||
func TestNullMeta(t *testing.T) {
|
||||
f, _ := New("zero")
|
||||
ctx := setupNullTestContext(t)
|
||||
f, _ := New(ctx, "zero")
|
||||
o, err := f.Meta(models.Meta{Key: "foo", Value: 42})
|
||||
be.Err(t, err, nil)
|
||||
be.Equal(t, o, empty)
|
||||
}
|
||||
|
||||
func TestNullEntry(t *testing.T) {
|
||||
f, _ := New("zero")
|
||||
ctx := setupNullTestContext(t)
|
||||
f, _ := New(ctx, "zero")
|
||||
o, err := f.Entry(models.Entry{Title: "title", Date: time.Now()})
|
||||
be.Err(t, err, nil)
|
||||
be.Equal(t, o, empty)
|
||||
}
|
||||
|
||||
func TestNullLog(t *testing.T) {
|
||||
f, _ := New("zero")
|
||||
ctx := setupNullTestContext(t)
|
||||
f, _ := New(ctx, "zero")
|
||||
o, err := f.Log(models.Log{Name: "jim", Entries: []models.Entry{{Title: "title", Date: time.Now()}}})
|
||||
be.Err(t, err, nil)
|
||||
be.Equal(t, o, empty)
|
||||
}
|
||||
|
||||
func TestNullLogs(t *testing.T) {
|
||||
f, _ := New("zero")
|
||||
ctx := setupNullTestContext(t)
|
||||
f, _ := New(ctx, "zero")
|
||||
o, err := f.Logs([]models.Log{{Name: "jim", Entries: []models.Entry{{Title: "title", Date: time.Now()}}}})
|
||||
be.Err(t, err, nil)
|
||||
be.Equal(t, o, empty)
|
||||
|
|
|
|||
|
|
@ -3,12 +3,11 @@ 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) {
|
||||
func newPlain(ff map[string]any) (Formatter, error) {
|
||||
return &PlainText{}, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,16 +3,27 @@ package formatters
|
|||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"codeberg.org/danjones000/my-log/config"
|
||||
"codeberg.org/danjones000/my-log/models"
|
||||
"codeberg.org/danjones000/my-log/tools"
|
||||
"github.com/nalgeon/be"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func setupPlainTestContext(t *testing.T) context.Context {
|
||||
t.Helper()
|
||||
v := viper.New()
|
||||
v.SetConfigType("toml")
|
||||
return config.AddToContext(t.Context(), v)
|
||||
}
|
||||
|
||||
func TestPlainLogs(t *testing.T) {
|
||||
ctx := setupPlainTestContext(t)
|
||||
m := []models.Meta{
|
||||
{Key: "foo", Value: "bar"},
|
||||
{Key: "baz", Value: 42},
|
||||
|
|
@ -29,7 +40,7 @@ func TestPlainLogs(t *testing.T) {
|
|||
l2 := models.Log{Name: "more-stuff", Entries: []models.Entry{e2}}
|
||||
logs := []models.Log{l, l2}
|
||||
|
||||
f, err := New("plain")
|
||||
f, err := New(ctx, "plain")
|
||||
be.Err(t, err, nil)
|
||||
|
||||
out, err := f.Logs(logs)
|
||||
|
|
@ -95,40 +106,46 @@ func TestPlainLogs(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPlainName(t *testing.T) {
|
||||
f, _ := New("plain")
|
||||
ctx := setupPlainTestContext(t)
|
||||
f, _ := New(ctx, "plain")
|
||||
be.Equal(t, f.Name(), "plain")
|
||||
}
|
||||
|
||||
func TestPlainLogNone(t *testing.T) {
|
||||
f, _ := New("plain")
|
||||
ctx := setupPlainTestContext(t)
|
||||
f, _ := New(ctx, "plain")
|
||||
out, err := f.Logs([]models.Log{})
|
||||
be.Err(t, err, nil)
|
||||
be.Equal(t, len(out), 0)
|
||||
}
|
||||
|
||||
func TestPlainLogNoEntries(t *testing.T) {
|
||||
f, _ := New("plain")
|
||||
ctx := setupPlainTestContext(t)
|
||||
f, _ := New(ctx, "plain")
|
||||
out, err := f.Log(models.Log{Name: "foo"})
|
||||
be.Err(t, err, nil)
|
||||
be.Equal(t, len(out), 0)
|
||||
}
|
||||
|
||||
func TestPlainMetaEmpty(t *testing.T) {
|
||||
f, _ := New("plain")
|
||||
ctx := setupPlainTestContext(t)
|
||||
f, _ := New(ctx, "plain")
|
||||
out, err := f.Meta(models.Meta{Key: "foo", Value: ""})
|
||||
be.Err(t, err, nil)
|
||||
be.Equal(t, len(out), 0)
|
||||
}
|
||||
|
||||
func TestPlainMetaError(t *testing.T) {
|
||||
f, _ := New("plain")
|
||||
ctx := setupPlainTestContext(t)
|
||||
f, _ := New(ctx, "plain")
|
||||
out, err := f.Meta(models.Meta{Key: "foo", Value: make(chan bool)})
|
||||
be.Err(t, err)
|
||||
be.Equal(t, len(out), 0)
|
||||
}
|
||||
|
||||
func TestPlainEntry(t *testing.T) {
|
||||
f, _ := New("plain")
|
||||
ctx := setupPlainTestContext(t)
|
||||
f, _ := New(ctx, "plain")
|
||||
now := time.Now()
|
||||
out, err := f.Entry(models.Entry{
|
||||
Title: "foo",
|
||||
|
|
|
|||
18
go.mod
18
go.mod
|
|
@ -3,25 +3,33 @@ module codeberg.org/danjones000/my-log
|
|||
go 1.26.0
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.3.2
|
||||
github.com/caarlos0/env/v10 v10.0.0
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/markusmobius/go-dateparser v1.2.3
|
||||
github.com/nalgeon/be v0.3.0
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/viper v1.21.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/elliotchance/pie/v2 v2.7.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||
github.com/hablullah/go-hijri v1.0.2 // indirect
|
||||
github.com/hablullah/go-juliandays v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958 // indirect
|
||||
github.com/magefile/mage v1.14.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/tetratelabs/wazero v1.2.1 // indirect
|
||||
github.com/wasilibs/go-re2 v1.3.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/exp v0.0.0-20220321173239-a90fa8a75705 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
)
|
||||
|
|
|
|||
47
go.sum
47
go.sum
|
|
@ -1,14 +1,16 @@
|
|||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/caarlos0/env/v10 v10.0.0 h1:yIHUBZGsyqCnpTkbjk8asUlx6RFhhEs+h7TOBdgdzXA=
|
||||
github.com/caarlos0/env/v10 v10.0.0/go.mod h1:ZfulV76NvVPw3tm591U4SwL3Xx9ldzBP9aGxzeN7G18=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/elliotchance/pie/v2 v2.7.0 h1:FqoIKg4uj0G/CrLGuMS9ejnFKa92lxE1dEgBD3pShXg=
|
||||
github.com/elliotchance/pie/v2 v2.7.0/go.mod h1:18t0dgGFH006g4eVdDtWfgFZPQEgl10IoEO8YWEq3Og=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hablullah/go-hijri v1.0.2 h1:drT/MZpSZJQXo7jftf5fthArShcaMtsal0Zf/dnmp6k=
|
||||
|
|
@ -19,31 +21,58 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
|
|||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958 h1:qxLoi6CAcXVzjfvu+KXIXJOAsQB62LXjsfbOaErsVzE=
|
||||
github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958/go.mod h1:Wqfu7mjUHj9WDzSSPI5KfBclTTEnLveRUFr/ujWnTgE=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
|
||||
github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||
github.com/markusmobius/go-dateparser v1.2.3 h1:TvrsIvr5uk+3v6poDjaicnAFJ5IgtFHgLiuMY2Eb7Nw=
|
||||
github.com/markusmobius/go-dateparser v1.2.3/go.mod h1:cMwQRrBUQlK1UI5TIFHEcvpsMbkWrQLXuaPNMFzuYLk=
|
||||
github.com/nalgeon/be v0.3.0 h1:QsPANqEtcOD5qT2S3KAtIkDBBn8SXUf/Lb5Bi/z4UqM=
|
||||
github.com/nalgeon/be v0.3.0/go.mod h1:PMwMuBLopwKJkSHnr2qHyLcZYUTqNejN7A8RAqNWO3E=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
||||
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/tetratelabs/wazero v1.2.1 h1:J4X2hrGzJvt+wqltuvcSjHQ7ujQxA9gb6PeMs4qlUWs=
|
||||
github.com/tetratelabs/wazero v1.2.1/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ=
|
||||
github.com/wasilibs/go-re2 v1.3.0 h1:LFhBNzoStM3wMie6rN2slD1cuYH2CGiHpvNL3UtcsMw=
|
||||
github.com/wasilibs/go-re2 v1.3.0/go.mod h1:AafrCXVvGRJJOImMajgJ2M7rVmWyisVK7sFshbxnVrg=
|
||||
github.com/wasilibs/nottinygc v0.4.0 h1:h1TJMihMC4neN6Zq+WKpLxgd9xCFMw7O9ETLwY2exJQ=
|
||||
github.com/wasilibs/nottinygc v0.4.0/go.mod h1:oDcIotskuYNMpqMF23l7Z8uzD4TC0WXHK8jetlB3HIo=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/exp v0.0.0-20220321173239-a90fa8a75705 h1:ba9YlqfDGTTQ5aZ2fwOoQ1hf32QySyQkR6ODGDzHlnE=
|
||||
golang.org/x/exp v0.0.0-20220321173239-a90fa8a75705/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue