syft/imgbom/pkg/catalog.go
2020-05-29 11:31:01 -04:00

145 lines
3.0 KiB
Go

package pkg
import (
"fmt"
"sync"
"github.com/anchore/imgbom/internal/log"
"github.com/anchore/stereoscope/pkg/file"
"github.com/blevesearch/bleve"
"github.com/blevesearch/bleve/mapping"
)
// TODO: add reader methods (by type, id, fuzzy search, etc)
var nextPackageID int64
type Catalog struct {
byID map[ID]*Package
byType map[Type][]*Package
byFile map[file.Reference][]*Package
searchSpace mapping.IndexMapping
nameSearchIndex bleve.Index
metadataSearchIndex bleve.Index
lock sync.RWMutex
}
func NewCatalog() Catalog {
searchMap := bleve.NewIndexMapping()
nameIndex, err := bleve.NewMemOnly(searchMap)
if err != nil {
// TODO: log
panic(err)
}
metadataIndex, err := bleve.NewMemOnly(searchMap)
if err != nil {
// TODO: log
panic(err)
}
return Catalog{
byID: make(map[ID]*Package),
byType: make(map[Type][]*Package),
byFile: make(map[file.Reference][]*Package),
searchSpace: searchMap,
nameSearchIndex: nameIndex,
metadataSearchIndex: metadataIndex,
}
}
func (c *Catalog) Package(id ID) *Package {
return c.byID[id]
}
func (c *Catalog) PackagesByFile(ref file.Reference) []*Package {
return c.byFile[ref]
}
func (c *Catalog) Add(p Package) {
if p.id != 0 {
log.Errorf("package already added to catalog: %s", p)
return
}
c.lock.Lock()
defer c.lock.Unlock()
p.id = ID(nextPackageID)
nextPackageID++
// store by package ID
c.byID[p.id] = &p
// store by package type
_, ok := c.byType[p.Type]
if !ok {
c.byType[p.Type] = make([]*Package, 0)
}
c.byType[p.Type] = append(c.byType[p.Type], &p)
// store by file references
for _, s := range p.Source {
_, ok := c.byFile[s]
if !ok {
c.byFile[s] = make([]*Package, 0)
}
c.byFile[s] = append(c.byFile[s], &p)
}
// index the package findings
err := c.nameSearchIndex.Index(fmt.Sprintf("%d", p.id), p.Name)
if err != nil {
// TODO: just no...
panic(err)
}
err = c.metadataSearchIndex.Index(fmt.Sprintf("%d", p.id), p.Metadata)
if err != nil {
// TODO: just no...
panic(err)
}
}
func (c *Catalog) SearchMetadata(query string) *bleve.SearchResult {
request := bleve.NewSearchRequest(bleve.NewMatchQuery(query))
result, err := c.metadataSearchIndex.Search(request)
if err != nil {
// TODO: just no...
panic(err)
}
return result
}
func (c *Catalog) SearchName(query string) *bleve.SearchResult {
request := bleve.NewSearchRequest(bleve.NewMatchQuery(query))
result, err := c.nameSearchIndex.Search(request)
if err != nil {
// TODO: just no...
panic(err)
}
return result
}
func (c *Catalog) Enumerate(types ...Type) <-chan *Package {
channel := make(chan *Package)
go func() {
defer close(channel)
for ty, packages := range c.byType {
if len(types) != 0 {
found := false
typeCheck:
for _, t := range types {
if t == ty {
found = true
break typeCheck
}
}
if !found {
continue
}
}
for _, p := range packages {
channel <- p
}
}
}()
return channel
}