From 993b2efe63e221cee550756770894c9c58474d25 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Thu, 2 Apr 2026 21:47:12 +0300 Subject: task 00g/00k/00h/008: gofmt, remove ProviderWithFallback, stdlib helpers, shared prompt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - task 00g: fix gofmt violations (trailing whitespace, missing newlines, indentation) in 8 files; all pass gofmt -l now - task 00k: remove unused ProviderWithFallback and its tests (YAGNI — no production caller existed; voice-level fallback via RunWithVoiceFallbacks already covers the real use case) - task 00h: replace private splitLines/trimSpace/isSpace helpers in internal/batch/processor.go with strings.Split+ReplaceAll and strings.TrimSpace from the stdlib; remove the now-redundant tests - task 008: extract buildEducationalPrompt into internal/image/prompt.go so the prompt-assembly policy (scene truncation cascade, char limit) lives in one place; both OpenAIClient and NanoBananaClient delegate to it Co-Authored-By: Claude Sonnet 4.6 --- internal/batch/processor.go | 49 ++------------ internal/batch/processor_test.go | 134 --------------------------------------- 2 files changed, 5 insertions(+), 178 deletions(-) (limited to 'internal/batch') diff --git a/internal/batch/processor.go b/internal/batch/processor.go index 314d67f..2b002a9 100644 --- a/internal/batch/processor.go +++ b/internal/batch/processor.go @@ -31,10 +31,11 @@ func ReadBatchFile(filename string) ([]WordEntry, error) { } var entries []WordEntry - lines := string(content) - - for _, line := range splitLines(lines) { - if line = trimSpace(line); line != "" { + // Normalize \r\n to \n before splitting so both Windows and Unix line + // endings are handled uniformly by strings.Split. + normalized := strings.ReplaceAll(string(content), "\r\n", "\n") + for _, line := range strings.Split(normalized, "\n") { + if line = strings.TrimSpace(line); line != "" { entry := parseBatchLine(line) if entry != nil { entries = append(entries, *entry) @@ -103,43 +104,3 @@ func parseBatchLine(line string) *WordEntry { } } -// splitLines splits a string by newlines, handling both \n and \r\n line endings. -// Uses strings.Builder to avoid per-character heap allocations from += concatenation. -func splitLines(s string) []string { - var lines []string - var current strings.Builder - for _, r := range s { - if r == '\n' { - lines = append(lines, current.String()) - current.Reset() - } else if r != '\r' { - current.WriteRune(r) - } - } - if current.Len() > 0 { - lines = append(lines, current.String()) - } - return lines -} - -// trimSpace trims whitespace from string -func trimSpace(s string) string { - start := 0 - end := len(s) - - // Trim from start - for start < end && isSpace(rune(s[start])) { - start++ - } - - // Trim from end - for end > start && isSpace(rune(s[end-1])) { - end-- - } - - return s[start:end] -} - -func isSpace(r rune) bool { - return r == ' ' || r == '\t' || r == '\n' || r == '\r' -} diff --git a/internal/batch/processor_test.go b/internal/batch/processor_test.go index fc4f7b0..8bc8079 100644 --- a/internal/batch/processor_test.go +++ b/internal/batch/processor_test.go @@ -163,137 +163,3 @@ func TestReadBatchFile_FileNotFound(t *testing.T) { } } -func TestSplitLines(t *testing.T) { - tests := []struct { - name string - input string - want []string - }{ - { - name: "unix line endings", - input: "line1\nline2\nline3", - want: []string{"line1", "line2", "line3"}, - }, - { - name: "windows line endings", - input: "line1\r\nline2\r\nline3", - want: []string{"line1", "line2", "line3"}, - }, - { - name: "mixed line endings", - input: "line1\nline2\r\nline3", - want: []string{"line1", "line2", "line3"}, - }, - { - name: "empty string", - input: "", - want: nil, - }, - { - name: "single line no ending", - input: "single line", - want: []string{"single line"}, - }, - { - name: "trailing newline", - input: "line1\nline2\n", - want: []string{"line1", "line2"}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := splitLines(tt.input) - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("splitLines() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestTrimSpace(t *testing.T) { - tests := []struct { - name string - input string - want string - }{ - { - name: "no whitespace", - input: "hello", - want: "hello", - }, - { - name: "leading spaces", - input: " hello", - want: "hello", - }, - { - name: "trailing spaces", - input: "hello ", - want: "hello", - }, - { - name: "both sides", - input: " hello ", - want: "hello", - }, - { - name: "tabs and spaces", - input: "\t hello \t", - want: "hello", - }, - { - name: "newlines", - input: "\nhello\n", - want: "hello", - }, - { - name: "all whitespace types", - input: " \t\n\rhello \t\n\r", - want: "hello", - }, - { - name: "empty string", - input: "", - want: "", - }, - { - name: "only whitespace", - input: " \t\n\r ", - want: "", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := trimSpace(tt.input) - if got != tt.want { - t.Errorf("trimSpace() = %q, want %q", got, tt.want) - } - }) - } -} - -func TestIsSpace(t *testing.T) { - tests := []struct { - r rune - want bool - }{ - {' ', true}, - {'\t', true}, - {'\n', true}, - {'\r', true}, - {'a', false}, - {'1', false}, - {'!', false}, - {0, false}, - } - - for _, tt := range tests { - t.Run(string(tt.r), func(t *testing.T) { - if got := isSpace(tt.r); got != tt.want { - t.Errorf("isSpace(%q) = %v, want %v", tt.r, got, tt.want) - } - }) - } -} -- cgit v1.2.3