github.com/tooploox/oya@v0.0.21-0.20230524103240-1cda1861aad6/pkg/deptree/deptree.go (about)

     1  package deptree
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/tooploox/oya/pkg/deptree/internal"
     7  	"github.com/tooploox/oya/pkg/errors"
     8  	"github.com/tooploox/oya/pkg/mvs"
     9  	"github.com/tooploox/oya/pkg/oyafile"
    10  	"github.com/tooploox/oya/pkg/pack"
    11  	"github.com/tooploox/oya/pkg/types"
    12  )
    13  
    14  type ErrResolvingDeps struct {
    15  }
    16  
    17  func (e ErrResolvingDeps) Error() string {
    18  	return "error resolving dependencies"
    19  }
    20  
    21  // DependencyTree defines a project's dependencies, allowing for loading them.
    22  type DependencyTree struct {
    23  	installDirs  []string
    24  	dependencies []pack.Pack
    25  	reqs         *internal.Reqs
    26  	rootDir      string
    27  }
    28  
    29  // New returns a new dependency tree.
    30  // BUG(bilus): It's called a 'tree' but it currently does not take into account inter-pack
    31  // dependencies. This will likely change and then the name will fit like a glove. ;)
    32  func New(rootDir string, installDirs []string, dependencies []pack.Pack) (*DependencyTree, error) {
    33  	return &DependencyTree{
    34  		installDirs:  installDirs,
    35  		dependencies: dependencies,
    36  		reqs:         internal.NewReqs(rootDir, installDirs),
    37  		rootDir:      rootDir,
    38  	}, nil
    39  }
    40  
    41  // Explode takes the initial list of dependencies and builds the full list,
    42  // taking into account packs' dependencies and using Minimal Version Selection.
    43  func (dt *DependencyTree) Explode() error {
    44  	list, err := mvs.List(dt.dependencies, dt.reqs)
    45  	if err != nil {
    46  		return errors.Wrap(
    47  			err,
    48  			ErrResolvingDeps{},
    49  			errors.Location{
    50  				Name:        dt.rootDir,
    51  				VerboseName: fmt.Sprintf("project at %q", dt.rootDir),
    52  			},
    53  		)
    54  	}
    55  	dt.dependencies = list
    56  	return nil
    57  }
    58  
    59  // Load loads an pack's Oyafile based on its import path.
    60  // It supports two types of import paths:
    61  // - referring to the project's Require: section (e.g. github.com/tooploox/oya-packs/docker), in this case it will load, the required version;
    62  // - path relative to the project's root (e.g. /) -- does not support versioning, loads Oyafile directly from the path (<root dir>/<import path>).
    63  func (dt *DependencyTree) Load(importPath types.ImportPath) (*oyafile.Oyafile, bool, error) {
    64  	pack, found, err := dt.findRequiredPack(importPath)
    65  	if err != nil {
    66  		return nil, false, err
    67  	}
    68  	if found {
    69  		return dt.reqs.LoadLocalOyafile(pack)
    70  	}
    71  	return nil, false, nil
    72  }
    73  
    74  // Find lookups pack by its import path.
    75  func (dt *DependencyTree) Find(importPath types.ImportPath) (pack.Pack, bool, error) {
    76  	for _, pack := range dt.dependencies {
    77  		if pack.ImportPath() == importPath {
    78  			return pack, true, nil
    79  		}
    80  	}
    81  	return pack.Pack{}, false, nil
    82  }
    83  
    84  // ForEach iterates through the packs.
    85  func (dt *DependencyTree) ForEach(f func(pack.Pack) error) error {
    86  	for _, pack := range dt.dependencies {
    87  		if err := f(pack); err != nil {
    88  			return err
    89  		}
    90  	}
    91  	return nil
    92  }
    93  
    94  func (dt *DependencyTree) findRequiredPack(importPath types.ImportPath) (pack.Pack, bool, error) {
    95  	for _, pack := range dt.dependencies {
    96  		if pack.ImportPath() == importPath {
    97  			return pack, true, nil
    98  		}
    99  	}
   100  	return pack.Pack{}, false, nil
   101  }