110 lines
1.8 KiB
Go
110 lines
1.8 KiB
Go
![]() |
package parser
|
||
|
|
||
|
import (
|
||
|
"archive/zip"
|
||
|
"context"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"io/ioutil"
|
||
|
"os"
|
||
|
|
||
|
"gitlab.com/gitlab-org/labkit/log"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
Lsif = "lsif"
|
||
|
)
|
||
|
|
||
|
type Parser struct {
|
||
|
Docs *Docs
|
||
|
|
||
|
pr *io.PipeReader
|
||
|
}
|
||
|
|
||
|
type Config struct {
|
||
|
TempPath string
|
||
|
}
|
||
|
|
||
|
func NewParser(ctx context.Context, r io.Reader, config Config) (io.ReadCloser, error) {
|
||
|
docs, err := NewDocs(config)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
// ZIP files need to be seekable. Don't hold it all in RAM, use a tempfile
|
||
|
tempFile, err := ioutil.TempFile(config.TempPath, Lsif)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
defer tempFile.Close()
|
||
|
|
||
|
if err := os.Remove(tempFile.Name()); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
size, err := io.Copy(tempFile, r)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
log.WithContextFields(ctx, log.Fields{"lsif_zip_cache_bytes": size}).Print("cached incoming LSIF zip on disk")
|
||
|
|
||
|
zr, err := zip.NewReader(tempFile, size)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if len(zr.File) == 0 {
|
||
|
return nil, errors.New("empty zip file")
|
||
|
}
|
||
|
|
||
|
file, err := zr.File[0].Open()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
defer file.Close()
|
||
|
|
||
|
if err := docs.Parse(file); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
pr, pw := io.Pipe()
|
||
|
parser := &Parser{
|
||
|
Docs: docs,
|
||
|
pr: pr,
|
||
|
}
|
||
|
|
||
|
go parser.transform(pw)
|
||
|
|
||
|
return parser, nil
|
||
|
}
|
||
|
|
||
|
func (p *Parser) Read(b []byte) (int, error) {
|
||
|
return p.pr.Read(b)
|
||
|
}
|
||
|
|
||
|
func (p *Parser) Close() error {
|
||
|
p.pr.Close()
|
||
|
|
||
|
return p.Docs.Close()
|
||
|
}
|
||
|
|
||
|
func (p *Parser) transform(pw *io.PipeWriter) {
|
||
|
zw := zip.NewWriter(pw)
|
||
|
|
||
|
if err := p.Docs.SerializeEntries(zw); err != nil {
|
||
|
zw.Close() // Free underlying resources only
|
||
|
pw.CloseWithError(fmt.Errorf("lsif parser: Docs.SerializeEntries: %v", err))
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if err := zw.Close(); err != nil {
|
||
|
pw.CloseWithError(fmt.Errorf("lsif parser: ZipWriter.Close: %v", err))
|
||
|
return
|
||
|
}
|
||
|
|
||
|
pw.Close()
|
||
|
}
|