Skip to content

Commit f93ce0b

Browse files
committed
max file size validation and improved language parsing warnings/ markdown output handling
1 parent 3dc134b commit f93ce0b

6 files changed

Lines changed: 88 additions & 27 deletions

File tree

c2mConfig/c2mConfig.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func InitializeConfigFromFlags() (*Config, error) {
7676
}
7777

7878
func IsConfigValid(config *Config) bool {
79-
return config != nil && config.InputFolder != ""
79+
return config != nil && config.InputFolder != "" && config.MaxFileSize > 0
8080
}
8181

8282
func LoadGitignorePatterns(path string) ([]string, error) {

c2mConfig/c2mConfig_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ func TestIsConfigValid(t *testing.T) {
1313
config *Config
1414
want bool
1515
}{
16-
{"valid config", &Config{InputFolder: "input", OutputMarkdown: "output"}, true},
17-
{"empty input folder", &Config{InputFolder: "", OutputMarkdown: "output"}, false},
18-
{"valid without output", &Config{InputFolder: "input", OutputMarkdown: ""}, true},
16+
{"valid config", &Config{InputFolder: "input", OutputMarkdown: "output", MaxFileSize: 1024}, true},
17+
{"empty input folder", &Config{InputFolder: "", OutputMarkdown: "output", MaxFileSize: 1024}, false},
18+
{"valid without output", &Config{InputFolder: "input", OutputMarkdown: "", MaxFileSize: 1024}, true},
1919
{"nil config", nil, false},
20+
{"zero max file size", &Config{InputFolder: "input", MaxFileSize: 0}, false},
21+
{"negative max file size", &Config{InputFolder: "input", MaxFileSize: -1}, false},
2022
}
2123

2224
for _, tt := range tests {

language/language.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package language
22

33
import (
4+
"fmt"
5+
"os"
46
"path/filepath"
57
"strings"
68
)
79

8-
var SupportedLanguages = map[string]bool{
10+
var supportedLanguages = map[string]bool{
911
".php": true,
1012
".go": true,
1113
".js": true,
@@ -37,13 +39,13 @@ func ParseLanguages(languages string) map[string]bool {
3739
result := make(map[string]bool)
3840

3941
if languages == "" {
40-
for lang, defaultEnabled := range SupportedLanguages {
42+
for lang, defaultEnabled := range supportedLanguages {
4143
result[lang] = defaultEnabled
4244
}
4345
return result
4446
}
4547

46-
for lang := range SupportedLanguages {
48+
for lang := range supportedLanguages {
4749
result[lang] = false
4850
}
4951

@@ -55,8 +57,10 @@ func ParseLanguages(languages string) map[string]bool {
5557
}
5658
lang = strings.ToLower(lang)
5759

58-
if _, exists := SupportedLanguages[lang]; exists {
60+
if _, exists := supportedLanguages[lang]; exists {
5961
result[lang] = true
62+
} else {
63+
fmt.Fprintf(os.Stderr, "Warning: unrecognized language %q, skipping\n", strings.TrimPrefix(lang, "."))
6064
}
6165
}
6266

@@ -126,7 +130,7 @@ func GetInactiveLanguages(allowedLanguages map[string]bool) []string {
126130

127131
func GetDefaultLanguages() []string {
128132
var defaults []string
129-
for lang, defaultEnabled := range SupportedLanguages {
133+
for lang, defaultEnabled := range supportedLanguages {
130134
if defaultEnabled {
131135
defaults = append(defaults, strings.TrimPrefix(lang, "."))
132136
}
@@ -136,7 +140,7 @@ func GetDefaultLanguages() []string {
136140

137141
func GetSupportedLanguages() []string {
138142
var languages []string
139-
for lang := range SupportedLanguages {
143+
for lang := range supportedLanguages {
140144
languages = append(languages, strings.TrimPrefix(lang, "."))
141145
}
142146
return languages

main_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ func TestRun(t *testing.T) {
2727
}
2828

2929
oldStdout := os.Stdout
30+
defer func() { os.Stdout = oldStdout }()
3031
r, w, _ := os.Pipe()
3132
os.Stdout = w
3233

@@ -106,6 +107,7 @@ func TestRun(t *testing.T) {
106107

107108
func TestDisplayVersion(t *testing.T) {
108109
oldStdout := os.Stdout
110+
defer func() { os.Stdout = oldStdout }()
109111
r, w, _ := os.Pipe()
110112
os.Stdout = w
111113

@@ -126,6 +128,7 @@ func TestDisplayVersion(t *testing.T) {
126128
func TestDisplayUsageInstructions(t *testing.T) {
127129
t.Run("with nil config", func(t *testing.T) {
128130
oldStdout := os.Stdout
131+
defer func() { os.Stdout = oldStdout }()
129132
r, w, _ := os.Pipe()
130133
os.Stdout = w
131134

@@ -152,6 +155,7 @@ func TestDisplayUsageInstructions(t *testing.T) {
152155
}
153156

154157
oldStdout := os.Stdout
158+
defer func() { os.Stdout = oldStdout }()
155159
r, w, _ := os.Pipe()
156160
os.Stdout = w
157161

@@ -171,6 +175,7 @@ func TestDisplayUsageInstructions(t *testing.T) {
171175

172176
t.Run("shows error when requested", func(t *testing.T) {
173177
oldStdout := os.Stdout
178+
defer func() { os.Stdout = oldStdout }()
174179
r, w, _ := os.Pipe()
175180
os.Stdout = w
176181

processor/processor.go

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type Options struct {
2020
}
2121

2222
func ProcessDirectory(opts Options, output io.Writer) error {
23-
processed := 0
23+
found := false
2424

2525
ret := filepath.WalkDir(opts.InputFolder, func(path string, d os.DirEntry, err error) error {
2626
if err != nil {
@@ -44,9 +44,9 @@ func ProcessDirectory(opts Options, output io.Writer) error {
4444
}
4545

4646
if !d.IsDir() && language.IsFileAllowed(d.Name(), opts.AllowedLanguages, opts.AllowedFileNames) {
47-
processed++
47+
found = true
4848
lang := language.GetMarkdownLanguage(d.Name(), opts.AllowedFileNames)
49-
return WriteMarkdown(path, output, lang, opts.MaxFileSize)
49+
return WriteMarkdown(path, relPath, output, lang, opts.MaxFileSize)
5050
}
5151

5252
return nil
@@ -56,27 +56,27 @@ func ProcessDirectory(opts Options, output io.Writer) error {
5656
return ret
5757
}
5858

59-
if processed == 0 {
59+
if !found {
6060
return errors.New("no files processed - file list is empty")
6161
}
6262

6363
return nil
6464
}
6565

66-
func WriteMarkdown(path string, output io.Writer, lang string, maxFileSize int64) error {
66+
func WriteMarkdown(path string, displayPath string, output io.Writer, lang string, maxFileSize int64) error {
6767
fileInfo, err := os.Stat(path)
6868
if err != nil {
6969
return fmt.Errorf("stating file %s: %w", path, err)
7070
}
7171

7272
if fileInfo.Size() > maxFileSize {
73-
fmt.Fprintf(os.Stderr, "Warning: skipping large file %s (%d bytes)\n", path, fileInfo.Size())
73+
fmt.Fprintf(os.Stderr, "Warning: skipping large file %s (%d bytes)\n", displayPath, fileInfo.Size())
7474
return nil
7575
}
7676

7777
var buf strings.Builder
7878
buf.WriteString("# ")
79-
buf.WriteString(path)
79+
buf.WriteString(displayPath)
8080
buf.WriteString("\n")
8181

8282
if lang != "md" {
@@ -87,19 +87,21 @@ func WriteMarkdown(path string, output io.Writer, lang string, maxFileSize int64
8787
return fmt.Errorf("writing header for %s: %w", path, err)
8888
}
8989

90-
file, err := os.Open(path)
90+
content, err := os.ReadFile(path)
9191
if err != nil {
92-
return fmt.Errorf("opening file %s: %w", path, err)
92+
return fmt.Errorf("reading file %s: %w", path, err)
9393
}
94-
defer file.Close()
9594

96-
if _, err := io.Copy(output, file); err != nil {
97-
return fmt.Errorf("copying content from %s: %w", path, err)
95+
if _, err := output.Write(content); err != nil {
96+
return fmt.Errorf("writing content from %s: %w", path, err)
9897
}
9998

10099
suffix := ""
101100
if lang != "md" {
102-
suffix = "\n```"
101+
if len(content) == 0 || content[len(content)-1] != '\n' {
102+
suffix = "\n"
103+
}
104+
suffix += "```"
103105
}
104106
suffix += "\n\n"
105107

processor/processor_test.go

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func TestWriteMarkdown(t *testing.T) {
2121
}
2222

2323
var output bytes.Buffer
24-
err = WriteMarkdown(inputFile, &output, "go", 100*1024*1024)
24+
err = WriteMarkdown(inputFile, inputFile, &output, "go", 100*1024*1024)
2525
if err != nil {
2626
t.Errorf("WriteMarkdown() error: %v", err)
2727
}
@@ -53,7 +53,7 @@ func TestWriteMarkdown(t *testing.T) {
5353
}
5454

5555
var output bytes.Buffer
56-
err = WriteMarkdown(inputFile, &output, "md", 100*1024*1024)
56+
err = WriteMarkdown(inputFile, inputFile, &output, "md", 100*1024*1024)
5757
if err != nil {
5858
t.Errorf("WriteMarkdown() error: %v", err)
5959
}
@@ -82,7 +82,7 @@ func TestWriteMarkdown(t *testing.T) {
8282
}
8383

8484
var output bytes.Buffer
85-
err = WriteMarkdown(inputFile, &output, "go", 100)
85+
err = WriteMarkdown(inputFile, inputFile, &output, "go", 100)
8686
if err != nil {
8787
t.Errorf("WriteMarkdown() should not error for large files: %v", err)
8888
}
@@ -92,9 +92,57 @@ func TestWriteMarkdown(t *testing.T) {
9292
}
9393
})
9494

95+
t.Run("adds newline before closing fence when file lacks trailing newline", func(t *testing.T) {
96+
tempDir := t.TempDir()
97+
inputFile := filepath.Join(tempDir, "test.go")
98+
99+
err := os.WriteFile(inputFile, []byte("package main"), 0644)
100+
if err != nil {
101+
t.Fatalf("Failed to create input file: %v", err)
102+
}
103+
104+
var output bytes.Buffer
105+
err = WriteMarkdown(inputFile, inputFile, &output, "go", 100*1024*1024)
106+
if err != nil {
107+
t.Errorf("WriteMarkdown() error: %v", err)
108+
}
109+
110+
contentStr := output.String()
111+
if strings.Contains(contentStr, "main```") {
112+
t.Error("Closing fence should not be on the same line as code")
113+
}
114+
if !strings.Contains(contentStr, "main\n```") {
115+
t.Error("Closing fence should be on its own line")
116+
}
117+
})
118+
119+
t.Run("uses display path in header", func(t *testing.T) {
120+
tempDir := t.TempDir()
121+
inputFile := filepath.Join(tempDir, "test.go")
122+
123+
err := os.WriteFile(inputFile, []byte("package main\n"), 0644)
124+
if err != nil {
125+
t.Fatalf("Failed to create input file: %v", err)
126+
}
127+
128+
var output bytes.Buffer
129+
err = WriteMarkdown(inputFile, "test.go", &output, "go", 100*1024*1024)
130+
if err != nil {
131+
t.Errorf("WriteMarkdown() error: %v", err)
132+
}
133+
134+
contentStr := output.String()
135+
if !strings.HasPrefix(contentStr, "# test.go\n") {
136+
t.Errorf("Header should use display path, got: %s", contentStr[:40])
137+
}
138+
if strings.Contains(contentStr, "# "+tempDir) {
139+
t.Error("Header should not contain absolute path")
140+
}
141+
})
142+
95143
t.Run("handles non-existent file", func(t *testing.T) {
96144
var output bytes.Buffer
97-
err := WriteMarkdown("/tmp/nonexistent/file.go", &output, "go", 100*1024*1024)
145+
err := WriteMarkdown("/tmp/nonexistent/file.go", "file.go", &output, "go", 100*1024*1024)
98146
if err == nil {
99147
t.Error("WriteMarkdown() should error for non-existent file")
100148
}

0 commit comments

Comments
 (0)