github.com/vanstinator/golangci-lint@v0.0.0-20240223191551-cc572f00d9d1/pkg/fsutils/linecache.go (about)

     1  package fsutils
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"sync"
     7  )
     8  
     9  type fileLinesCache [][]byte
    10  
    11  type LineCache struct {
    12  	files     sync.Map
    13  	fileCache *FileCache
    14  }
    15  
    16  func NewLineCache(fc *FileCache) *LineCache {
    17  	return &LineCache{
    18  		fileCache: fc,
    19  	}
    20  }
    21  
    22  // GetLine returns the index1-th (1-based index) line from the file on filePath
    23  func (lc *LineCache) GetLine(filePath string, index1 int) (string, error) {
    24  	if index1 == 0 { // some linters, e.g. gosec can do it: it really means first line
    25  		index1 = 1
    26  	}
    27  
    28  	const index1To0Offset = -1
    29  	rawLine, err := lc.getRawLine(filePath, index1+index1To0Offset)
    30  	if err != nil {
    31  		return "", err
    32  	}
    33  
    34  	return string(bytes.Trim(rawLine, "\r")), nil
    35  }
    36  
    37  func (lc *LineCache) getRawLine(filePath string, index0 int) ([]byte, error) {
    38  	fc, err := lc.getFileCache(filePath)
    39  	if err != nil {
    40  		return nil, fmt.Errorf("failed to get file %s lines cache: %w", filePath, err)
    41  	}
    42  
    43  	if index0 < 0 {
    44  		return nil, fmt.Errorf("invalid file line index0 < 0: %d", index0)
    45  	}
    46  
    47  	if index0 >= len(fc) {
    48  		return nil, fmt.Errorf("invalid file line index0 (%d) >= len(fc) (%d)", index0, len(fc))
    49  	}
    50  
    51  	return fc[index0], nil
    52  }
    53  
    54  func (lc *LineCache) getFileCache(filePath string) (fileLinesCache, error) {
    55  	loadedFc, ok := lc.files.Load(filePath)
    56  	if ok {
    57  		return loadedFc.(fileLinesCache), nil
    58  	}
    59  
    60  	fileBytes, err := lc.fileCache.GetFileBytes(filePath)
    61  	if err != nil {
    62  		return nil, fmt.Errorf("can't get file %s bytes from cache: %w", filePath, err)
    63  	}
    64  
    65  	fc := bytes.Split(fileBytes, []byte("\n"))
    66  	lc.files.Store(filePath, fileLinesCache(fc))
    67  	return fc, nil
    68  }