github.com/anchore/syft@v1.38.2/syft/internal/fileresolver/excluding_file.go (about)

     1  package fileresolver
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  
     8  	"github.com/anchore/syft/syft/file"
     9  )
    10  
    11  type excludeFn func(string) bool
    12  
    13  // excluding decorates a resolver with an exclusion function that is used to
    14  // filter out entries in the delegate resolver
    15  type excluding struct {
    16  	delegate  file.Resolver
    17  	excludeFn excludeFn
    18  }
    19  
    20  // NewExcludingDecorator create a new resolver which wraps the provided delegate and excludes
    21  // entries based on a provided path exclusion function
    22  func NewExcludingDecorator(delegate file.Resolver, excludeFn excludeFn) file.Resolver {
    23  	return &excluding{
    24  		delegate,
    25  		excludeFn,
    26  	}
    27  }
    28  
    29  func (r *excluding) FileContentsByLocation(location file.Location) (io.ReadCloser, error) {
    30  	if locationMatches(&location, r.excludeFn) {
    31  		return nil, fmt.Errorf("no such location: %+v", location.RealPath)
    32  	}
    33  	return r.delegate.FileContentsByLocation(location)
    34  }
    35  
    36  func (r *excluding) FileMetadataByLocation(location file.Location) (file.Metadata, error) {
    37  	if locationMatches(&location, r.excludeFn) {
    38  		return file.Metadata{}, fmt.Errorf("no such location: %+v", location.RealPath)
    39  	}
    40  	return r.delegate.FileMetadataByLocation(location)
    41  }
    42  
    43  func (r *excluding) HasPath(path string) bool {
    44  	if r.excludeFn(path) {
    45  		return false
    46  	}
    47  	return r.delegate.HasPath(path)
    48  }
    49  
    50  func (r *excluding) FilesByPath(paths ...string) ([]file.Location, error) {
    51  	locations, err := r.delegate.FilesByPath(paths...)
    52  	return filterLocations(locations, err, r.excludeFn)
    53  }
    54  
    55  func (r *excluding) FilesByGlob(patterns ...string) ([]file.Location, error) {
    56  	locations, err := r.delegate.FilesByGlob(patterns...)
    57  	return filterLocations(locations, err, r.excludeFn)
    58  }
    59  
    60  func (r *excluding) FilesByMIMEType(types ...string) ([]file.Location, error) {
    61  	locations, err := r.delegate.FilesByMIMEType(types...)
    62  	return filterLocations(locations, err, r.excludeFn)
    63  }
    64  
    65  func (r *excluding) RelativeFileByPath(location file.Location, path string) *file.Location {
    66  	l := r.delegate.RelativeFileByPath(location, path)
    67  	if l != nil && locationMatches(l, r.excludeFn) {
    68  		return nil
    69  	}
    70  	return l
    71  }
    72  
    73  func (r *excluding) AllLocations(ctx context.Context) <-chan file.Location {
    74  	c := make(chan file.Location)
    75  	go func() {
    76  		defer close(c)
    77  		for location := range r.delegate.AllLocations(ctx) {
    78  			if !locationMatches(&location, r.excludeFn) {
    79  				select {
    80  				case <-ctx.Done():
    81  					return
    82  				case c <- location:
    83  					continue
    84  				}
    85  			}
    86  		}
    87  	}()
    88  	return c
    89  }
    90  
    91  func locationMatches(location *file.Location, exclusionFn excludeFn) bool {
    92  	return exclusionFn(location.RealPath) || exclusionFn(location.AccessPath)
    93  }
    94  
    95  func filterLocations(locations []file.Location, err error, exclusionFn excludeFn) ([]file.Location, error) {
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	if exclusionFn != nil {
   100  		for i := 0; i < len(locations); i++ {
   101  			location := &locations[i]
   102  			if locationMatches(location, exclusionFn) {
   103  				locations = append(locations[:i], locations[i+1:]...)
   104  				i--
   105  			}
   106  		}
   107  	}
   108  	return locations, nil
   109  }