github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/analyzers/rust/rust.go (about)

     1  // Package rust provides analysers for Rust projects.
     2  //
     3  // A `BuildTarget` in Rust is the directory of the Rust project containing `Cargo.lock`.
     4  
     5  package rust
     6  
     7  import (
     8  	"os"
     9  	"path/filepath"
    10  
    11  	"github.com/apex/log"
    12  	"github.com/mitchellh/mapstructure"
    13  
    14  	"github.com/fossas/fossa-cli/buildtools/cargo"
    15  	"github.com/fossas/fossa-cli/errors"
    16  	"github.com/fossas/fossa-cli/files"
    17  	"github.com/fossas/fossa-cli/graph"
    18  	"github.com/fossas/fossa-cli/module"
    19  	"github.com/fossas/fossa-cli/pkg"
    20  )
    21  
    22  type Analyzer struct {
    23  	Module  module.Module
    24  	Options Options
    25  }
    26  
    27  type Options struct {
    28  	Strategy string `mapstructure:"strategy"`
    29  }
    30  
    31  func New(m module.Module) (*Analyzer, error) {
    32  	log.WithField("options", m.Options).Debug("constructing analyzer")
    33  
    34  	var options Options
    35  	err := mapstructure.Decode(m.Options, &options)
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  	log.WithField("options", options).Debug("parsed analyzer options")
    40  
    41  	return &Analyzer{
    42  		Module:  m,
    43  		Options: options,
    44  	}, nil
    45  }
    46  
    47  // Discover constructs modules in all directories with a `Cargo.lock`.
    48  func Discover(dir string, options map[string]interface{}) ([]module.Module, error) {
    49  	var modules []module.Module
    50  	err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
    51  		if err != nil {
    52  			log.WithError(err).WithField("path", path).Debug("error while walking for discovery")
    53  		}
    54  
    55  		if !info.IsDir() && info.Name() == "Cargo.lock" {
    56  			log.WithFields(log.Fields{
    57  				"path": path,
    58  				"name": path,
    59  			}).Debug("found Rust module")
    60  			relPath, err := filepath.Rel(dir, path)
    61  			if err != nil {
    62  				return errors.Wrap(err, "error discovering rust modules")
    63  			}
    64  			modules = append(modules, module.Module{
    65  				Name:        path,
    66  				Type:        pkg.Rust,
    67  				BuildTarget: filepath.Dir(relPath),
    68  				Dir:         filepath.Dir(relPath),
    69  			})
    70  		}
    71  		return nil
    72  	})
    73  	if err != nil {
    74  		return nil, errors.Wrap(err, "Could not find Rust package manifests:")
    75  	}
    76  
    77  	return modules, nil
    78  }
    79  
    80  // Clean logs a warning and does nothing for Rust.
    81  func (a *Analyzer) Clean() error {
    82  	log.Warnf("Clean is not implemented for Rust")
    83  	return nil
    84  }
    85  
    86  func (a *Analyzer) Build() error {
    87  	return errors.New("Build is not implemented for Rust")
    88  }
    89  
    90  func (a *Analyzer) IsBuilt() (bool, error) {
    91  	return files.Exists(a.Module.Dir, "Cargo.lock")
    92  }
    93  
    94  func (a *Analyzer) Analyze() (graph.Deps, error) {
    95  	switch a.Options.Strategy {
    96  	case "manifest":
    97  		// TODO: Add for filetree scanning.
    98  		fallthrough
    99  	case "cargo-tree":
   100  		// TODO: Check for `cargo-tree` command and parse output.
   101  		// This will give the most accurate results.
   102  		fallthrough
   103  	case "lockfile":
   104  		fallthrough
   105  	default:
   106  		return cargo.LockfileDependencies("Cargo.lock", a.Module.Dir)
   107  	}
   108  }