Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,6 @@ linters:
path: internal/aggregator/aggregator\.go
text: "(ineffectual assignment to cm|SA4006)"

# Unused: zlibPool field in PackReader is reserved for Wave 3 Track C reader pooling
- linters:
- unused
path: internal/gitengine/pack\.go
text: "field zlibPool is unused"

# Staticcheck: Empty branch in integration test awaiting Wave 3 exit code assertion
- linters:
- staticcheck
Expand Down
27 changes: 22 additions & 5 deletions internal/filter/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package filter
import (
"fmt"
"path/filepath"
"slices"
"sort"
"strings"
)
Expand All @@ -13,8 +14,10 @@ type FileTypeDef struct {
Filenames []string
}

// BuiltinTypes provides standard file type mappings matching ripgrep conventions.
var BuiltinTypes = map[string]FileTypeDef{
// builtinTypes provides standard file type mappings matching ripgrep conventions.
// Treat it as immutable: read it only through LookupType and ListTypes, which
// hand out defensive copies so callers cannot mutate the table.
var builtinTypes = map[string]FileTypeDef{
"c": {Extensions: []string{".c", ".h"}},
"cpp": {Extensions: []string{".cpp", ".cc", ".cxx", ".c++", ".hpp", ".hh", ".hxx", ".h++"}},
"css": {Extensions: []string{".css", ".scss", ".sass", ".less"}},
Expand Down Expand Up @@ -75,7 +78,7 @@ func NewTypeMatcher(types []string) (*TypeMatcher, error) {
continue
}

def, ok := BuiltinTypes[typeName]
def, ok := LookupType(typeName)
if !ok {
return nil, fmt.Errorf("unknown file type %q (use --type-list to view supported types)", t)
}
Expand Down Expand Up @@ -113,10 +116,24 @@ func (tm *TypeMatcher) Match(path string) bool {
return false
}

// LookupType returns the definition for the named file type. The returned
// FileTypeDef owns copies of its slices, so callers may mutate them without
// corrupting the builtin table.
func LookupType(name string) (FileTypeDef, bool) {
def, ok := builtinTypes[name]
if !ok {
return FileTypeDef{}, false
}
return FileTypeDef{
Extensions: slices.Clone(def.Extensions),
Filenames: slices.Clone(def.Filenames),
}, true
}

// ListTypes returns a sorted list of all supported file type names.
func ListTypes() []string {
names := make([]string, 0, len(BuiltinTypes))
for name := range BuiltinTypes {
names := make([]string, 0, len(builtinTypes))
for name := range builtinTypes {
names = append(names, name)
}
sort.Strings(names)
Expand Down
44 changes: 44 additions & 0 deletions internal/filter/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,47 @@ func TestTypeMatcher(t *testing.T) {
t.Errorf("expected at least 20 supported types, got %d", len(types))
}
}

func TestLookupTypeReturnsCopy(t *testing.T) {
def, ok := LookupType("docker")
if !ok {
t.Fatal("expected docker to be a known type")
}
if len(def.Extensions) == 0 || len(def.Filenames) == 0 {
t.Fatalf("docker definition should carry extensions and filenames, got %+v", def)
}

wantExt := def.Extensions[0]
wantName := def.Filenames[0]

// A caller mutating the returned slices must not corrupt the builtin table.
def.Extensions[0] = ".corrupted"
def.Filenames[0] = "Corrupted"

again, ok := LookupType("docker")
if !ok {
t.Fatal("docker disappeared from the builtin table")
}
if again.Extensions[0] != wantExt {
t.Errorf("Extensions mutation leaked: got %q, want %q", again.Extensions[0], wantExt)
}
if again.Filenames[0] != wantName {
t.Errorf("Filenames mutation leaked: got %q, want %q", again.Filenames[0], wantName)
}

// Matching must still work after the mutation attempt.
tm, err := NewTypeMatcher([]string{"docker"})
if err != nil {
t.Fatalf("NewTypeMatcher failed: %v", err)
}
if !tm.Match("Dockerfile") {
t.Error("Dockerfile stopped matching after a caller mutated a LookupType result")
}
if !tm.Match("my.dockerfile") {
t.Error("my.dockerfile stopped matching after a caller mutated a LookupType result")
}

if _, ok := LookupType("nonexistenttype"); ok {
t.Error("expected LookupType to report an unknown type as missing")
}
}
6 changes: 4 additions & 2 deletions internal/gitengine/pack.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import (
"sync"
)

// packMagic is the 4-byte header magic of a .pack file.
const packMagic = "PACK"

var (
packMagic = []byte{'P', 'A', 'C', 'K'}
// ErrPackInvalid indicates a malformed or unsupported packfile.
ErrPackInvalid = errors.New("invalid packfile")
)
Expand Down Expand Up @@ -68,7 +70,7 @@ func OpenPackfile(packPath, idxPath string) (*PackReader, error) {
return nil, fmt.Errorf("%w: failed to read pack header: %v", ErrPackInvalid, err)
}

if string(hdr[:4]) != string(packMagic) {
if string(hdr[:4]) != packMagic {
_ = f.Close()
return nil, fmt.Errorf("%w: invalid pack magic %q", ErrPackInvalid, string(hdr[:4]))
}
Expand Down
12 changes: 6 additions & 6 deletions internal/gitengine/pack_index.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import (
"os"
)

var (
idxV2Magic = []byte{0xff, 0x74, 0x4f, 0x63} // \xfftOc
// ErrIdxInvalid indicates an invalid or unsupported .idx file.
ErrIdxInvalid = errors.New("invalid pack index file")
)
// idxV2Magic is the 4-byte header magic of a .idx v2 file ("\xfftOc").
const idxV2Magic = "\xff\x74\x4f\x63"

// ErrIdxInvalid indicates an invalid or unsupported .idx file.
var ErrIdxInvalid = errors.New("invalid pack index file")

// PackIndex represents a parsed Git packfile .idx v2 file.
type PackIndex struct {
Expand Down Expand Up @@ -43,7 +43,7 @@ func ParsePackIndex(data []byte) (*PackIndex, error) {
}

// 1. Verify magic and version
if !bytes.Equal(data[:4], idxV2Magic) {
if string(data[:4]) != idxV2Magic {
return nil, fmt.Errorf("%w: invalid header magic %x", ErrIdxInvalid, data[:4])
}
version := binary.BigEndian.Uint32(data[4:8])
Expand Down
2 changes: 1 addition & 1 deletion internal/gitengine/pack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func buildTestPackAndIdx(objects []testPackObj) (packData []byte, idxData []byte
})

var idxBuf bytes.Buffer
idxBuf.Write(idxV2Magic)
idxBuf.WriteString(idxV2Magic)
_ = binary.Write(&idxBuf, binary.BigEndian, uint32(2))

// Fanout table
Expand Down
Loading