diff options
Diffstat (limited to 'workhorse/internal/lsif_transformer/parser/parser.go')
-rw-r--r-- | workhorse/internal/lsif_transformer/parser/parser.go | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/workhorse/internal/lsif_transformer/parser/parser.go b/workhorse/internal/lsif_transformer/parser/parser.go new file mode 100644 index 00000000000..085e7a856aa --- /dev/null +++ b/workhorse/internal/lsif_transformer/parser/parser.go @@ -0,0 +1,109 @@ +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() +} |