package main

import (
	
	

	
)

type state struct {
	ByModule     map[string][]*packages.Package
	ModuleByPath map[string]packages.Module
	Modules      []packages.Module
}

func ( string,  ...string) (*state, error) {
	log("loading packages")
	,  := loadPackages(, ...)
	if  != nil {
		return nil, 
	}
	log("loaded packages: " + strconv.Itoa(len()))
	return stateFromPacakges(), nil
}

func ( []*packages.Package) *state {
	,  := packagesByModule()
	 := sortedModules()
	return &state{
		ByModule:     ,
		ModuleByPath: ,
		Modules:      ,
	}
}

// loadPackages loads packages from the workspace that match the given pattern.
func ( string,  ...string) ([]*packages.Package, error) {
	,  := packages.Load(&packages.Config{
		Mode: packages.NeedModule |
			packages.NeedName |
			packages.NeedTypes |
			packages.NeedImports |
			packages.NeedDeps,
		Dir:   ,
		Tests: true,
	}, ...)
	if  != nil {
		return nil, 
	}
	return collectPackages(), nil
}

// collectPackages visits all packages and returns flattened list.
func ( []*packages.Package) []*packages.Package {
	var  []*packages.Package
	packages.Visit(, nil, func( *packages.Package) {
		 = append(, )
	})
	return 
}

// packagesByModule groups packages by their containing Go module. It skips
// packages that do not have modules (i.e. stdlib).
func ( []*packages.Package) (map[string][]*packages.Package, map[string]packages.Module) {
	 := make(map[string][]*packages.Package)
	 := make(map[string]packages.Module)
	for ,  := range  {
		if .Module == nil {
			continue
		}
		 := *.Module
		 := .Path
		[] = append([], )
		[] = 
	}
	return , 
}

// sortedModules returns modules sorted by their module path.
func ( map[string]packages.Module) []packages.Module {
	 := make([]packages.Module, 0, len())
	for ,  := range  {
		 = append(, )
	}
	sort.Slice(, func(,  int) bool {
		return [].Path < [].Path
	})
	return 
}

// splitBrokenPackages splits packages into good and broken.
func ( []*packages.Package) (,  []*packages.Package) {
	for ,  := range  {
		if len(.Errors) != 0 {
			 = append(, )
			continue
		}
		 = append(, )
	}
	return
}

// packagesByImportPath groups packages by their import path.
func ( []*packages.Package) map[string]*packages.Package {
	 := make(map[string]*packages.Package, len())
	for ,  := range  {
		[.PkgPath] = 
	}
	return 
}

// intersectPackageSets returns a list of import paths that are present in both
// sets.
func (,  map[string]*packages.Package) []string {
	var  []string
	for  := range  {
		if ,  := []; ! {
			continue
		}
		 = append(, )
	}
	sort.Strings()
	return 
}

// packagesNotInSet returns packages that are not present in the given set.
func ( []*packages.Package,  map[string]*packages.Package) []*packages.Package {
	var  []*packages.Package
	for ,  := range  {
		if ,  := [.PkgPath];  {
			continue
		}
		 = append(, )
	}
	return 
}

// intersectModuleSets returns a list of modules that are present in both sets.
func (,  map[string]packages.Module) []string {
	var  []string
	for  := range  {
		if ,  := []; ! {
			continue
		}
		 = append(, )
	}
	sort.Strings()
	return 
}

// modulesNotInSet returns a list of module versions that are not present
// in the moduleByPath set.
func ( []packages.Module,  map[string]packages.Module) []packages.Module {
	var  []packages.Module
	for ,  := range  {
		if ,  := [.Path];  {
			continue
		}
		 = append(, )
	}
	return 
}