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..90a3f53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +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 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 100% rename from internal/cmd/drop.go rename to cmd/drop.go 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/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..e69c476 100644 --- a/tools/parse.go +++ b/tools/parse.go @@ -11,20 +11,15 @@ 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 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 {