Initial commit

This commit is contained in:
Ismo Vuorinen
2025-02-07 09:15:37 +02:00
commit b9e2218305
19 changed files with 873 additions and 0 deletions

9
fileproc/collector.go Normal file
View File

@@ -0,0 +1,9 @@
// Package fileproc provides functions for collecting and processing files.
package fileproc
// CollectFiles scans the given root directory using the default walker (ProdWalker)
// and returns a slice of file paths.
func CollectFiles(root string) ([]string, error) {
var w Walker = ProdWalker{}
return w.Walk(root)
}

View File

@@ -0,0 +1,47 @@
package fileproc
import (
"os"
"testing"
)
func TestCollectFilesWithFakeWalker(t *testing.T) {
// Instead of using the production walker, use FakeWalker.
expectedFiles := []string{
"/path/to/file1.txt",
"/path/to/file2.go",
}
fake := FakeWalker{
Files: expectedFiles,
Err: nil,
}
// Use fake.Walk directly.
files, err := fake.Walk("dummyRoot")
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(files) != len(expectedFiles) {
t.Fatalf("Expected %d files, got %d", len(expectedFiles), len(files))
}
for i, f := range files {
if f != expectedFiles[i] {
t.Errorf("Expected file %s, got %s", expectedFiles[i], f)
}
}
}
func TestCollectFilesError(t *testing.T) {
// Fake walker returns an error.
fake := FakeWalker{
Files: nil,
Err: os.ErrNotExist,
}
_, err := fake.Walk("dummyRoot")
if err == nil {
t.Fatal("Expected an error, got nil")
}
}

16
fileproc/fake_walker.go Normal file
View File

@@ -0,0 +1,16 @@
// Package fileproc provides functions for file processing.
package fileproc
// FakeWalker implements Walker for testing purposes.
type FakeWalker struct {
Files []string
Err error
}
// Walk returns predetermined file paths or an error, depending on FakeWalker's configuration.
func (fw FakeWalker) Walk(root string) ([]string, error) {
if fw.Err != nil {
return nil, fw.Err
}
return fw.Files, nil
}

27
fileproc/processor.go Normal file
View File

@@ -0,0 +1,27 @@
// Package fileproc provides functions for processing files.
package fileproc
import (
"fmt"
"io/ioutil"
"github.com/sirupsen/logrus"
)
// WriteRequest represents the content to be written.
type WriteRequest struct {
Content string
}
// ProcessFile reads the file at filePath and sends a formatted output to outCh.
// The optional wg parameter is used when the caller wants to wait on file-level processing.
func ProcessFile(filePath string, outCh chan<- WriteRequest, wg *interface{}) {
content, err := ioutil.ReadFile(filePath)
if err != nil {
logrus.Errorf("Failed to read file %s: %v", filePath, err)
return
}
// Format: separator, file path, then content.
formatted := fmt.Sprintf("\n---\n%s\n%s\n", filePath, string(content))
outCh <- WriteRequest{Content: formatted}
}

View File

@@ -0,0 +1,45 @@
package fileproc
import (
"os"
"strings"
"sync"
"testing"
)
func TestProcessFile(t *testing.T) {
// Create a temporary file with known content.
tmpFile, err := os.CreateTemp("", "testfile")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmpFile.Name())
content := "Test content"
if _, err := tmpFile.WriteString(content); err != nil {
t.Fatal(err)
}
tmpFile.Close()
ch := make(chan WriteRequest, 1)
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
ProcessFile(tmpFile.Name(), ch, nil)
}()
wg.Wait()
close(ch)
var result string
for req := range ch {
result = req.Content
}
if !strings.Contains(result, tmpFile.Name()) {
t.Errorf("Output does not contain file path: %s", tmpFile.Name())
}
if !strings.Contains(result, content) {
t.Errorf("Output does not contain file content: %s", content)
}
}

40
fileproc/walker.go Normal file
View File

@@ -0,0 +1,40 @@
// Package fileproc provides functions for file processing.
package fileproc
import (
"github.com/boyter/gocodewalker"
"github.com/sirupsen/logrus"
)
// Walker defines an interface for scanning directories.
type Walker interface {
Walk(root string) ([]string, error)
}
// ProdWalker implements Walker using gocodewalker.
type ProdWalker struct{}
// Walk scans the given root directory using gocodewalker and returns a slice of file paths.
func (pw ProdWalker) Walk(root string) ([]string, error) {
fileListQueue := make(chan *gocodewalker.File, 100)
fileWalker := gocodewalker.NewFileWalker(root, fileListQueue)
errorHandler := func(err error) bool {
logrus.Errorf("error walking directory: %s", err.Error())
return true
}
fileWalker.SetErrorHandler(errorHandler)
go func() {
err := fileWalker.Start()
if err != nil {
logrus.Errorf("error walking directory: %s", err.Error())
}
}()
var files []string
for f := range fileListQueue {
files = append(files, f.Location)
}
return files, nil
}

21
fileproc/writer.go Normal file
View File

@@ -0,0 +1,21 @@
// Package fileproc provides functions for writing file contents concurrently.
package fileproc
import (
"io"
"os"
"github.com/sirupsen/logrus"
)
// StartWriter listens on the write channel and writes content to outFile.
// When finished, it signals on the done channel.
func StartWriter(outFile *os.File, writeCh <-chan WriteRequest, done chan<- struct{}) {
writer := io.Writer(outFile)
for req := range writeCh {
if _, err := writer.Write([]byte(req.Content)); err != nil {
logrus.Errorf("Error writing to file: %v", err)
}
}
done <- struct{}{}
}

31
fileproc/writer_test.go Normal file
View File

@@ -0,0 +1,31 @@
package fileproc
import (
"bytes"
"sync"
"testing"
)
func TestStartWriter(t *testing.T) {
var buf bytes.Buffer
writeCh := make(chan WriteRequest)
done := make(chan struct{})
go StartWriter(&buf, writeCh, done)
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
writeCh <- WriteRequest{Content: "Hello"}
writeCh <- WriteRequest{Content: " World"}
}()
wg.Wait()
close(writeCh)
<-done
if buf.String() != "Hello World" {
t.Errorf("Expected 'Hello World', got '%s'", buf.String())
}
}