package model
import (
"fmt"
"path/filepath"
"sort"
"strings"
)
type DepGraph struct {
Vendor string
Name string
Version string
Language Language
Path string
Licenses []string
licenseMap map[string]bool
Develop bool
Direct bool
Parents []*DepGraph
pset map[*DepGraph]bool
Children []*DepGraph
cset map[*DepGraph]bool
Expand any
}
func (dep *DepGraph) AppendChild(child *DepGraph) {
if dep == nil || child == nil {
return
}
if dep.cset == nil {
dep.cset = map[*DepGraph]bool{}
}
if child.pset == nil {
child.pset = map[*DepGraph]bool{}
}
if !dep.cset[child] {
dep.Children = append(dep.Children, child)
dep.cset[child] = true
}
if !child.pset[dep] {
child.Parents = append(child.Parents, dep)
child.pset[dep] = true
}
}
func (dep *DepGraph) RemoveChild(child *DepGraph) {
for i, c := range dep.Children {
if c == child {
dep.Children = append(dep.Children[:i], dep.Children[i+1:]...)
break
}
}
for i, p := range child.Parents {
if p == dep {
child.Parents = append(child.Parents[:i], child.Parents[i+1:]...)
break
}
}
delete(dep.cset, child)
delete(child.pset, dep)
}
func (dep *DepGraph) AppendLicense(lic string) {
if dep.licenseMap == nil {
dep.licenseMap = map[string]bool{}
}
if lic == "" {
return
}
if dep.licenseMap[lic] {
return
}
dep.licenseMap[lic] = true
dep.Licenses = append(dep.Licenses, lic)
}
func (dep *DepGraph) Index() string {
if dep.Vendor == "" {
return fmt.Sprintf("[%s:%s]", dep.Name, dep.Version)
}
return fmt.Sprintf("[%s:%s:%s]", dep.Vendor, dep.Name, dep.Version)
}
func (dep *DepGraph) String() string {
dev := ""
if dep.Develop {
dev = "<dev>"
}
return fmt.Sprintf("%s%s<%s>(%s)", dev, dep.Index(), dep.Language, dep.Path)
}
func (dep *DepGraph) Flush() {
parentMap := map[*DepGraph]bool{}
trueParentMap := map[*DepGraph]bool{}
dep.ForEachPath(func(p, n *DepGraph) bool {
for _, p := range n.Parents {
parentMap[p] = true
}
if p != nil {
trueParentMap[p] = true
}
return true
})
for p := range parentMap {
if trueParentMap[p] {
continue
}
for _, c := range p.Children {
p.RemoveChild(c)
}
}
dep.ForEachNode(func(p, n *DepGraph) bool { n.Expand = nil; return true })
dep.ForEachNode(func(p, n *DepGraph) bool {
if len(n.Parents) == 0 || n.Develop {
n.Expand = struct{}{}
}
return true
})
dep.ForEachPath(func(p, n *DepGraph) bool {
if n.Expand != nil {
return true
}
n.Develop = p.Develop
if !p.Develop {
n.Expand = struct{}{}
}
return true
})
dep.ForEachNode(func(p, n *DepGraph) bool {
if !n.Develop {
for _, p := range n.Parents {
if p.Develop {
p.RemoveChild(n)
}
}
}
return true
})
}
func (dep *DepGraph) Build(deep bool, lan Language) {
dep.Flush()
dep.ForEach(deep, false, false, func(p, n *DepGraph) bool {
if p != nil && n.Path == "" {
n.Path = p.Path
}
if n.Name != "" {
n.Path = filepath.Join(n.Path, n.Index())
}
if n.Language == Lan_None {
n.Language = lan
}
if len(n.Parents) == 0 || len(p.Parents) == 0 {
n.Direct = true
}
return true
})
}
func (dep *DepGraph) RemoveDevelop() {
dep.ForEachNode(func(p, n *DepGraph) bool {
if n.Develop {
for _, c := range n.Children {
n.RemoveChild(c)
}
for _, p := range n.Parents {
p.RemoveChild(n)
}
n = nil
return false
}
return true
})
}
func (dep *DepGraph) Tree(path, name bool) string {
if dep == nil {
return ""
}
sb := strings.Builder{}
dep.ForEach(true, path, name, func(p, n *DepGraph) bool {
if p == nil {
n.Expand = 0
} else {
n.Expand = p.Expand.(int) + 1
}
sb.WriteString(strings.Repeat(" ", n.Expand.(int)))
sb.WriteString(n.String())
sb.WriteString("\n")
return true
})
return sb.String()
}
func (dep *DepGraph) ForEach(deep, path, name bool, do func(p, n *DepGraph) bool) {
if dep == nil {
return
}
var set func(p, n *DepGraph) bool
if path {
pathSet := map[*DepGraph]map[*DepGraph]bool{}
set = func(p, n *DepGraph) bool {
if _, ok := pathSet[p]; !ok {
pathSet[p] = map[*DepGraph]bool{}
}
if pathSet[p][n] {
return true
}
pathSet[p][n] = true
return false
}
} else {
nodeSet := map[*DepGraph]bool{}
set = func(p, n *DepGraph) bool {
if nodeSet[n] {
return true
}
nodeSet[n] = true
return false
}
}
type pn struct {
p *DepGraph
n *DepGraph
}
q := []*pn{{nil, dep}}
for len(q) > 0 {
var n *pn
if deep {
n = q[len(q)-1]
q = q[:len(q)-1]
} else {
n = q[0]
q = q[1:]
}
if set(n.p, n.n) || !do(n.p, n.n) {
continue
}
next := make([]*DepGraph, len(n.n.Children))
copy(next, n.n.Children)
if name {
sort.Slice(next, func(i, j int) bool { return next[i].Name < next[j].Name })
}
if deep {
for i, j := 0, len(next)-1; i < j; i, j = i+1, j-1 {
next[i], next[j] = next[j], next[i]
}
}
for _, c := range next {
q = append(q, &pn{n.n, c})
}
}
}
func (dep *DepGraph) ForEachPath(do func(p, n *DepGraph) bool) {
dep.ForEach(false, true, false, do)
}
func (dep *DepGraph) ForEachNode(do func(p, n *DepGraph) bool) {
dep.ForEach(false, false, false, do)
}
type DepGraphMap struct {
m map[string]*DepGraph
key func(...string) string
store func(...string) *DepGraph
}
func NewDepGraphMap(key func(...string) string, store func(...string) *DepGraph) *DepGraphMap {
if key == nil {
key = func(s ...string) string { return strings.Join(s, ":") }
}
return &DepGraphMap{key: key, store: store, m: map[string]*DepGraph{}}
}
func (s *DepGraphMap) Range(do func(k string, v *DepGraph) bool) {
for k, v := range s.m {
if !do(k, v) {
break
}
}
}
func (s *DepGraphMap) LoadOrStore(words ...string) *DepGraph {
if s == nil || s.key == nil || s.store == nil {
return nil
}
key := s.key(words...)
dep, ok := s.m[key]
if !ok {
dep = s.store(words...)
s.m[key] = dep
}
return dep
}