diff options
| author | Martin Kagamino Lehoux <martin@lehoux.net> | 2025-07-20 14:16:34 +0200 |
|---|---|---|
| committer | Martin Kagamino Lehoux <martin@lehoux.net> | 2025-07-20 14:16:34 +0200 |
| commit | 6a7215688babd968edc05d0ddb2caaefe92a5ca3 (patch) | |
| tree | cb6b4a756331d11dbab7bf6af932deeaebd11856 /cmd/kdev | |
| parent | 972dd7453f8429d291bfb4dc7cba58a8900cd7cb (diff) | |
refactor(kdev): Extract algo & interfaces
Diffstat (limited to 'cmd/kdev')
| -rw-r--r-- | cmd/kdev/main.go | 51 | ||||
| -rw-r--r-- | cmd/kdev/record_extractor.go | 92 |
2 files changed, 97 insertions, 46 deletions
diff --git a/cmd/kdev/main.go b/cmd/kdev/main.go index e72b37c..8163041 100644 --- a/cmd/kdev/main.go +++ b/cmd/kdev/main.go @@ -13,8 +13,6 @@ import ( "strings" "time" - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/plumbing/object" "github.com/martinlehoux/kagamigo/kcore" "github.com/samber/lo" "github.com/samber/lo/mutable" @@ -25,10 +23,10 @@ import ( ) type Record struct { - keyword string - path string - line int - date time.Time + keyword string + relativePath string + line int + date time.Time } var ( @@ -108,13 +106,10 @@ func main() { } slog.Info("Scanning repository", "repo", repoPath, "sort", sortBy, "max", maxRecords, "after", after.Format(time.DateOnly), "before", before.Format(time.DateOnly), "algo", algo) - repo, err := git.PlainOpen(path.Join(repoPath, ".git")) - kcore.Expect(err, "Error opening repository") - ref, err := repo.Head() - kcore.Expect(err, "Error getting HEAD") - head, err := repo.CommitObject(ref.Hash()) - kcore.Expect(err, "Error getting commit object") - kcore.Expect(walkRepo(keywords, head, &records), "Error walking directory") + factory, err := CreateRecordExtractorFactory(algo, repoPath) + kcore.Expect(err, "Error creating record extractor factory") + + kcore.Expect(walkRepo(keywords, factory, &records), "Error walking directory") if !after.IsZero() { records = lo.Filter(records, func(record Record, index int) bool { return record.date.After(after) }) @@ -129,7 +124,7 @@ func main() { } fmt.Println("") for _, record := range records[:min(maxRecords, len(records))] { - fmt.Printf("%s\t %s:%d\n", record.date.Format("2006-01-02"), record.path, record.line) + fmt.Printf("%s\t %s:%d\n", record.date.Format(time.DateOnly), record.relativePath, record.line) } } @@ -161,7 +156,7 @@ func isBinaryFile(absolutePath, relativePath string) bool { return false } -func walkRepo(keywords []string, head *object.Commit, records *[]Record) error { +func walkRepo(keywords []string, factory RecordExtractorFactory, records *[]Record) error { progress := progressbar.Default(-1, "Scanning") return filepath.WalkDir(repoPath, func(path string, d fs.DirEntry, err error) error { relativePath := strings.TrimPrefix(path, repoPath) @@ -186,7 +181,7 @@ func walkRepo(keywords []string, head *object.Commit, records *[]Record) error { return nil } kcore.Expect(progress.Add(1), "Error incrementing progress") - return processFile(repoPath, relativePath, keywords, records, head) + return processFile(repoPath, relativePath, keywords, records, factory) }) } @@ -195,7 +190,7 @@ type MatchingLine struct { keyword string } -func processFile(repoPath string, relativePath string, keywords []string, records *[]Record, head *object.Commit) error { +func processFile(repoPath string, relativePath string, keywords []string, records *[]Record, factory RecordExtractorFactory) error { absolutePath := path.Join(repoPath, relativePath) content, err := os.ReadFile(absolutePath) // #nosec G304 if err != nil { @@ -215,22 +210,22 @@ func processFile(repoPath string, relativePath string, keywords []string, record return nil } - // TODO: Extract outer - var recordExtractor RecordExtractor - switch algo { - case "git": - recordExtractor = &GitRecordExtractor{repoPath: repoPath} - case "go-git": - recordExtractor = NewGoGitRecordExtractor(head, relativePath) - case "stat": - recordExtractor = NewStatRecordExtractor(absolutePath) + recordExtractor, err := factory.CreateExtractor(relativePath) + switch err { + case ErrNotTracked: + return nil + case nil: + break + default: + return kcore.Wrap(err, "Error creating record extractor") } - kcore.Assert(recordExtractor != nil, "wrong algo value (git, go-git, stat)") + for _, line := range matchingLines { - record, err := recordExtractor.Extract(line.keyword, relativePath, line.line) + record, err := recordExtractor.Extract(line.keyword, line.line) if err != nil { slog.Warn("Error extracting record", "err", err) } else { + record.relativePath = relativePath *records = append(*records, record) } } diff --git a/cmd/kdev/record_extractor.go b/cmd/kdev/record_extractor.go index a44d6c6..05a5e5c 100644 --- a/cmd/kdev/record_extractor.go +++ b/cmd/kdev/record_extractor.go @@ -5,6 +5,7 @@ import ( "io/fs" "os" "os/exec" + "path" "strconv" "strings" "time" @@ -14,27 +15,84 @@ import ( "github.com/martinlehoux/kagamigo/kcore" ) +type RecordExtractorFactory interface { + CreateExtractor(relativePath string) (RecordExtractor, error) +} + type RecordExtractor interface { - Extract(keyword string, path string, line int) (Record, error) + Extract(keyword string, line int) (Record, error) } -type StatRecordExtractor struct { - fileInfo fs.FileInfo +var ErrNotTracked = fmt.Errorf("file is not tracked by git") + +func CreateRecordExtractorFactory(algo, repoPath string) (RecordExtractorFactory, error) { + switch algo { + case "git": + return &GitRecordExtractorFactory{repoPath: repoPath}, nil + case "go-git": + return NewGoGitRecordExtractorFactory(repoPath) + case "stat": + return &StatRecordExtractorFactory{repoPath: repoPath}, nil + default: + return nil, fmt.Errorf("unsupported algorithm: %s", algo) + } +} + +type GitRecordExtractorFactory struct { + repoPath string +} + +func (f *GitRecordExtractorFactory) CreateExtractor(relativePath string) (RecordExtractor, error) { + cmd := exec.Command("git", "ls-files", "--error-unmatch", relativePath) + cmd.Dir = f.repoPath + if err := cmd.Run(); err != nil { + return nil, ErrNotTracked + } + return &GitRecordExtractor{repoPath: f.repoPath, relativePath: relativePath}, nil +} + +type GoGitRecordExtractorFactory struct { + head *object.Commit +} + +func NewGoGitRecordExtractorFactory(repoPath string) (*GoGitRecordExtractorFactory, error) { + repo, err := git.PlainOpen(path.Join(repoPath, ".git")) + kcore.Expect(err, "Error opening repository") + ref, err := repo.Head() + kcore.Expect(err, "Error getting HEAD") + head, err := repo.CommitObject(ref.Hash()) + kcore.Expect(err, "Error getting commit object") + return &GoGitRecordExtractorFactory{head: head}, nil } -func NewStatRecordExtractor(absolutePath string) *StatRecordExtractor { - fileInfo, err := os.Stat(absolutePath) +func (f *GoGitRecordExtractorFactory) CreateExtractor(relativePath string) (RecordExtractor, error) { + blame, err := git.Blame(f.head, relativePath) + if err == object.ErrFileNotFound { + return nil, err + } + return &GoGitRecordExtractor{blame: blame}, nil +} + +type StatRecordExtractorFactory struct { + repoPath string +} + +func (f *StatRecordExtractorFactory) CreateExtractor(relativePath string) (RecordExtractor, error) { + fileInfo, err := os.Stat(path.Join(f.repoPath, relativePath)) kcore.Expect(err, "Error getting file info") return &StatRecordExtractor{ fileInfo: fileInfo, - } + }, nil } -func (re *StatRecordExtractor) Extract(keyword string, path string, line int) (Record, error) { +type StatRecordExtractor struct { + fileInfo fs.FileInfo +} + +func (re *StatRecordExtractor) Extract(keyword string, line int) (Record, error) { return Record{ keyword: keyword, - path: path, line: line, date: re.fileInfo.ModTime(), }, nil @@ -44,8 +102,8 @@ type GoGitRecordExtractor struct { blame *git.BlameResult } -func NewGoGitRecordExtractor(head *object.Commit, path string) *GoGitRecordExtractor { - blame, err := git.Blame(head, path) +func NewGoGitRecordExtractor(head *object.Commit, relativePath string) *GoGitRecordExtractor { + blame, err := git.Blame(head, relativePath) if err == object.ErrFileNotFound { return nil } @@ -54,32 +112,30 @@ func NewGoGitRecordExtractor(head *object.Commit, path string) *GoGitRecordExtra } } -func (re *GoGitRecordExtractor) Extract(keyword string, path string, line int) (Record, error) { +func (re *GoGitRecordExtractor) Extract(keyword string, line int) (Record, error) { record := Record{ keyword: keyword, - path: path, line: line, + date: re.blame.Lines[line-1].Date, } - record.date = re.blame.Lines[line-1].Date return record, nil } type GitRecordExtractor struct { - repoPath string + repoPath string + relativePath string } -func (re *GitRecordExtractor) Extract(keyword string, path string, line int) (Record, error) { +func (re *GitRecordExtractor) Extract(keyword string, line int) (Record, error) { record := Record{ keyword: keyword, - path: path, line: line, } - gitArgs := []string{"blame", "-L", fmt.Sprintf("%d,%d", line, line), "--porcelain", path} + gitArgs := []string{"blame", "-L", fmt.Sprintf("%d,%d", line, line), "--porcelain", re.relativePath} cmd := exec.Command("git", gitArgs...) // #nosec G204 cmd.Dir = re.repoPath output, err := cmd.Output() - if err != nil { return record, kcore.Wrap(err, fmt.Sprintf("Error running: git %s", strings.Join(gitArgs, " "))) } |
