diff --git a/.gitignore b/.gitignore index 5a806ae..3a550dc 100644 --- a/.gitignore +++ b/.gitignore @@ -122,6 +122,6 @@ Temporary Items *.icloud # End of https://www.toptal.com/developers/gitignore/api/go,linux,emacs,macos -/my-log +my-log cover.html cmd/test.go diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index c10b6e7..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,47 +0,0 @@ -# Agent Guidelines for my-log - -## Build/Test/Lint Commands -- Build: `make build` or `go build -o my-log` -- Test all: `make test` (runs fmt + test with race + coverage) -- Test single: `go test ./path/to/package -run TestName` -- Format: `make fmt` or `go fmt ./...` -- Coverage report: `make report` (generates cover.html) - -## Code Style -- **Module**: `codeberg.org/danjones000/my-log` -- **Go version**: 1.21.5 -- **Imports**: Standard library first, then external packages, then local packages. Use short aliases for common imports (e.g., `fp` for `path/filepath`, `mapst` for `mitchellh/mapstructure`) -- **Formatting**: Always run `go fmt` before commits (included in test target) -- **Types**: Use explicit types (e.g., `int64`, `float64`). Convert numbers appropriately when unmarshaling JSON -- **Naming**: PascalCase for exported, camelCase for unexported. Use descriptive names -- **Error handling**: Return wrapped errors with context. Define custom errors in models/errors.go. Use `ErrorIs` for error checking -- **Testing**: Use testify/assert. Table-driven tests with helper functions. Test both marshal/unmarshal for encoding types -- **Concurrency**: Use channels and goroutines with WaitGroups for parallel processing (see entry.go patterns) -- **Comments**: Include license header on cmd files. Document exported functions and types - -## Git Commit Guidelines -- **Format**: Prepend commit messages with a gitmoji emoji (see https://gitmoji.dev) -- **Style**: Write detailed commit messages that explain what changed and why -- **Examples**: `✨ Add JSON export functionality for log entries`, `🐛 Fix date parsing for RFC3339 timestamps`, `📝 Update README with configuration examples` - -## Git Flow Workflow -- **Main branches**: `stable` (production-ready), `develop` (integration branch) -- **Development**: Always commit new features/fixes to `develop` branch or appropriate feature branches -- **Branch prefixes**: - - `feat/feature-name` - New features, merge to `develop` when complete - - `bug/bug-name` - Bug fixes (non-urgent), merge to `develop` when complete - - `hot/version` - Hotfixes for production issues, merge to **both** `stable` and `develop` - - `rel/version` - Release preparation branches -- **Version tags**: Prefix all version tags with `v` (e.g., `v1.0.2`, `v0.0.6`) -- **Releases**: Update CHANGELOG.md with a summary of changes for each new version -- **Never commit directly to**: `stable` branch (only merge from `rel/` or `hot/` branches) -- **Before starting work**: Ensure you're on `develop` branch or create an appropriate feature branch from it - -## Project Structure -- `cmd/my-log/`: Main application entrypoint -- `internal/cmd/`: Cobra commands (root, drop, config) -- `models/`: Core types (Entry, Log, Meta) with marshal/unmarshal implementations -- `config/`: TOML-based configuration with env overrides -- `formatters/`: Output formatters (plain, json, null) -- `files/`: File operations (append) -- `tools/`: Utilities (date parsing, string parsing, write buffers) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46990e7..19ef977 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,24 +1,5 @@ # Changelog -## [0.0.7] - 2025-11-05 - -- 🚚 Refactor project structure to follow standard Go layout (cmd/my-log/ and internal/cmd/) -- 📝 Add AGENTS.md with comprehensive guidelines for coding agents -- ⚡️ Performance improvement: compile regexes only once -- ✨ Add support for bang prefix to skip value parsing -- 🐛 Fix bang trimming in output -- 🔨 Fix Makefile for Darwin compatibility -- ✏️ Fix Makefile to avoid unnecessary reruns -- ✅ Fix TestMkdirErr on Darwin - -## [0.0.6] - 2024-10-07 - -- Update external dependency: go-dateparser - -## [0.0.5] - 2024-10-07 - -- Small change: adds --output_json to drop command. - ## [0.0.4] - 2024-05-08 - ✨ Add -p flag to config to print config path diff --git a/Makefile b/Makefile index aaf684f..35491dc 100644 --- a/Makefile +++ b/Makefile @@ -3,12 +3,6 @@ OUT=my-log GOBIN=$(shell go env GOBIN) COVEROUT=cover.out COVERHTML=cover.html -OPEN=xdg-open -OS=$(shell uname -s) - -ifeq ($(OS),Darwin) -OPEN=open -endif .PHONY: help help: ## Show help for documented recipes @@ -29,8 +23,7 @@ test: ## Test application and generate coverage report $(MAKE) clean $(MAKE) $(COVEROUT) -$(COVEROUT): $(SOURCES) - $(MAKE) fmt +$(COVEROUT): $(SOURCES) fmt go test ./... -race -cover -coverprofile $@ $(COVERHTML): $(COVEROUT) @@ -40,11 +33,11 @@ $(COVERHTML): $(COVEROUT) report: $(COVERHTML) ## Generate a coverage report .PHONY: open-report -open-report: $(COVERHTML) ## Open the coverage report in the default browser - $(OPEN) $< +open-report: report ## Open the coverage report in the default browser + xdg-open $(COVERHTML) .PHONY: build build: $(OUT) ## Builds the application $(OUT): $(SOURCES) fmt - go build -o $@ ./cmd/my-log + go build -o $@ diff --git a/internal/cmd/config.go b/cmd/config.go similarity index 100% rename from internal/cmd/config.go rename to cmd/config.go diff --git a/internal/cmd/drop.go b/cmd/drop.go similarity index 92% rename from internal/cmd/drop.go rename to cmd/drop.go index 2c246e6..d74917e 100644 --- a/internal/cmd/drop.go +++ b/cmd/drop.go @@ -21,7 +21,6 @@ import ( "fmt" "time" - "codeberg.org/danjones000/my-log/config" "codeberg.org/danjones000/my-log/files" "codeberg.org/danjones000/my-log/formatters" "codeberg.org/danjones000/my-log/models" @@ -32,7 +31,6 @@ import ( var d Date var fields map[string]string var j Json -var outJson bool // dropCmd represents the drop command var dropCmd = &cobra.Command{ @@ -42,10 +40,6 @@ var dropCmd = &cobra.Command{ Args: cobra.ExactArgs(2), SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { - if outJson { - config.Overrides["output.stdout.config.format"] = "json" - } - log := args[0] title := args[1] ms := &models.Metas{} @@ -89,7 +83,6 @@ func init() { dropCmd.Flags().VarP(&d, "date", "d", "Date for log entry") dropCmd.Flags().StringToStringVarP(&fields, "fields", "f", nil, "Fields you add to entry") dropCmd.Flags().VarP(&j, "json", "j", "Entire entry as json") - dropCmd.Flags().BoolVarP(&outJson, "output_json", "o", false, "Output result as JSON") } type Json struct { diff --git a/internal/cmd/root.go b/cmd/root.go similarity index 100% rename from internal/cmd/root.go rename to cmd/root.go diff --git a/files/append_test.go b/files/append_test.go index 03f0253..0976363 100644 --- a/files/append_test.go +++ b/files/append_test.go @@ -211,7 +211,7 @@ func (s *AppendTestSuite) TestConfLoadErr() { func (s *AppendTestSuite) TestMkdirErr() { // Don't run this test as root - config.Overrides["input.path"] = "/var/my-logs-test" + config.Overrides["input.path"] = "/root/my-logs-test" defer func(path string) { config.Overrides["input.path"] = path }(s.dir) diff --git a/go.mod b/go.mod index b67cb17..4928514 100644 --- a/go.mod +++ b/go.mod @@ -5,10 +5,10 @@ go 1.21.5 require ( github.com/BurntSushi/toml v1.3.2 github.com/caarlos0/env/v10 v10.0.0 - github.com/markusmobius/go-dateparser v1.2.3 + github.com/markusmobius/go-dateparser v1.2.1 github.com/mitchellh/mapstructure v1.5.0 github.com/spf13/cobra v1.8.0 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.8.4 ) require ( @@ -25,7 +25,9 @@ require ( github.com/tetratelabs/wazero v1.2.1 // indirect github.com/wasilibs/go-re2 v1.3.0 // indirect golang.org/x/exp v0.0.0-20220321173239-a90fa8a75705 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/text v0.10.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/markusmobius/go-dateparser => github.com/goodevilgenius/go-dateparser v1.2.2 diff --git a/go.sum b/go.sum index f782cde..350aa50 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/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/goodevilgenius/go-dateparser v1.2.2 h1:Up9KokPx/h07mesQGAZQg3Xi/8yrDVn1638h3k/lRyk= +github.com/goodevilgenius/go-dateparser v1.2.2/go.mod h1:5xYsZ1h7iB3sE1BSu8bkjYpbFST7EU1/AFxcyO3mgYg= github.com/hablullah/go-hijri v1.0.2 h1:drT/MZpSZJQXo7jftf5fthArShcaMtsal0Zf/dnmp6k= github.com/hablullah/go-hijri v1.0.2/go.mod h1:OS5qyYLDjORXzK4O1adFw9Q5WfhOcMdAKglDkcTxgWQ= github.com/hablullah/go-juliandays v1.0.0 h1:A8YM7wIj16SzlKT0SRJc9CD29iiaUzpBLzh5hr0/5p0= @@ -22,8 +24,6 @@ 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/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -36,8 +36,8 @@ 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/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 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= @@ -46,8 +46,8 @@ github.com/wasilibs/nottinygc v0.4.0 h1:h1TJMihMC4neN6Zq+WKpLxgd9xCFMw7O9ETLwY2e github.com/wasilibs/nottinygc v0.4.0/go.mod h1:oDcIotskuYNMpqMF23l7Z8uzD4TC0WXHK8jetlB3HIo= 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/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 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= diff --git a/cmd/my-log/main.go b/main.go similarity index 93% rename from cmd/my-log/main.go rename to main.go index c7f2435..a23ee3c 100644 --- a/cmd/my-log/main.go +++ b/main.go @@ -16,7 +16,7 @@ along with this program. If not, see . */ package main -import "codeberg.org/danjones000/my-log/internal/cmd" +import "codeberg.org/danjones000/my-log/cmd" func main() { cmd.Execute() diff --git a/tools/parse.go b/tools/parse.go index 618737e..021c6a2 100644 --- a/tools/parse.go +++ b/tools/parse.go @@ -11,25 +11,24 @@ func ParseBytes(in []byte) any { return ParseString(string(in)) } -var yesno = regexp.MustCompile("^(y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)$") -var yes = regexp.MustCompile("^(y|Y|yes|Yes|YES|true|True|TRUE|on|On|ON)$") -var null = regexp.MustCompile("^(~|null|Null|NULL|none|None|NONE|nil|Nil|NIL)$") - func ParseString(in string) any { s := strings.TrimSpace(in) if s == "" { return s } - if strings.HasPrefix(s, "!") { - return s - } - + yesno := regexp.MustCompile("^(y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)$") + yes := regexp.MustCompile("^(y|Y|yes|Yes|YES|true|True|TRUE|on|On|ON)$") + null := regexp.MustCompile("^(~|null|Null|NULL|none|None|NONE|nil|Nil|NIL)$") var j json.RawMessage if null.MatchString(s) { return nil } else if yesno.MatchString(s) { - return yes.MatchString(s) + if yes.MatchString(s) { + return true + } else { + return false + } } else if i, err := strconv.Atoi(s); err == nil { return i } else if f, err := strconv.ParseFloat(s, 64); err == nil { diff --git a/tools/parse_date.go b/tools/parse_date.go index 0fa117a..d060299 100644 --- a/tools/parse_date.go +++ b/tools/parse_date.go @@ -1,7 +1,6 @@ package tools import ( - "strconv" "time" dp "github.com/markusmobius/go-dateparser" @@ -24,19 +23,6 @@ func ParseDate(in string) (t time.Time, err error) { return MaxTime, nil } - var er error - for _, format := range []string{time.RFC3339, DateFormat} { - if t, er = time.ParseInLocation(format, in, nil); er == nil { - return - } - } - - var ts int64 - if ts, er = strconv.ParseInt(in, 10, 0); er == nil { - t = time.Unix(ts, 0) - return - } - conf := dp.Configuration{ CurrentTime: time.Now().Local(), ReturnTimeAsPeriod: true, @@ -46,11 +32,12 @@ func ParseDate(in string) (t time.Time, err error) { d, err := dp.Parse(&conf, in) t = d.Time if err != nil { + d, err = dp.Parse(&conf, in, DateFormat) + t = d.Time return } y, mon, day, h, loc := t.Year(), t.Month(), t.Day(), t.Hour(), t.Location() - switch d.Period { case date.Second: t = t.Truncate(time.Second) diff --git a/tools/parse_date_test.go b/tools/parse_date_test.go index 3099b88..7aa30b2 100644 --- a/tools/parse_date_test.go +++ b/tools/parse_date_test.go @@ -23,7 +23,7 @@ func TestParseDate(t *testing.T) { firstMonth := time.Date(y, mon, 1, 0, 0, 0, 0, loc) firstYear := time.Date(y, 1, 1, 0, 0, 0, 0, loc) exact := "2075-02-12T12:13:54.536-02:00" - exactd, _ := time.ParseInLocation(time.RFC3339, exact, nil) + exactd, _ := time.ParseInLocation(time.RFC3339, exact, time.FixedZone("UTC-02:00", -7200)) var ts int64 = 1708876012 tsd := time.Unix(ts, 0) ent := "February 25, 2024 at 04:00:13AM +0230" diff --git a/tools/parse_test.go b/tools/parse_test.go index 72fc50d..826af9b 100644 --- a/tools/parse_test.go +++ b/tools/parse_test.go @@ -38,10 +38,6 @@ func TestParse(t *testing.T) { {"on-value", "on", true}, {"no-value", "no", false}, {"off-value", "off", false}, - {"skip-parsing-num", "!42", "!42"}, - {"skip-parsing-bool", "!false", "!false"}, - {"skip-parsing-time", "!" + when.Format(time.RFC3339), "!" + when.Format(time.RFC3339)}, - {"skip-parsing-duration", "!15 mins", "!15 mins"}, } for _, tt := range tests { diff --git a/tools/write_buffer.go b/tools/write_buffer.go index b9626bc..8ae9386 100644 --- a/tools/write_buffer.go +++ b/tools/write_buffer.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "strconv" - "strings" "time" ) @@ -28,9 +27,6 @@ func WriteValue(buff *bytes.Buffer, val any) (n int, err error) { return buff.Write(o) } case string: - if strings.HasPrefix(v, "!") { - return buff.WriteString(strings.TrimPrefix(v, "!")) - } return buff.WriteString(v) case int: return buff.WriteString(strconv.Itoa(v)) @@ -43,9 +39,6 @@ func WriteValue(buff *bytes.Buffer, val any) (n int, err error) { case json.RawMessage: return buff.Write(v) case []byte: - if v[0] == '!' { - return buff.Write(v[1:]) - } return buff.Write(v) case byte: err = buff.WriteByte(v) diff --git a/tools/write_buffer_test.go b/tools/write_buffer_test.go index 8d3ab1f..aa7e203 100644 --- a/tools/write_buffer_test.go +++ b/tools/write_buffer_test.go @@ -33,14 +33,6 @@ func TestWriteBuffer(t *testing.T) { {"slice", []any{1, 2, "foo"}, `[1,2,"foo"]`, nil}, {"map", map[string]any{"baz": 42, "foo": "bar"}, `{"baz":42,"foo":"bar"}`, nil}, {"struct", struct{}{}, "", errors.New("Unsupported type struct {}")}, - {"skip-bang-num", "!42", "42", nil}, - {"skip-bang-bool", "!false", "false", nil}, - {"skip-bang-time", "!" + when.Format(time.RFC3339), when.Format(time.RFC3339), nil}, - {"skip-bang-duration", "!15 mins", "15 mins", nil}, - {"skip-bang-bytes-num", []byte("!42"), "42", nil}, - {"skip-bang-bytes-bool", []byte("!false"), "false", nil}, - {"skip-bang-bytes-time", []byte("!" + when.Format(time.RFC3339)), when.Format(time.RFC3339), nil}, - {"skip-bang-bytes-duration", []byte("!15 mins"), "15 mins", nil}, } for _, tt := range tests {