Skip to content

Commit 49d2d7e

Browse files
committed
Added dependency graph
1 parent 5461bd7 commit 49d2d7e

File tree

2 files changed

+170
-0
lines changed

2 files changed

+170
-0
lines changed

pkg/provider/dep/dep.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/* implements a dependency graph algorithm */
2+
package dep
3+
4+
import (
5+
"fmt"
6+
"slices"
7+
)
8+
9+
///////////////////////////////////////////////////////////////////////////////
10+
// TYPES
11+
12+
type Node any
13+
14+
type node struct {
15+
Node
16+
edges []Node
17+
}
18+
19+
type dep struct {
20+
// Node depends on several other nodes
21+
edges map[Node][]Node
22+
}
23+
24+
///////////////////////////////////////////////////////////////////////////////
25+
// LIFECYCLE
26+
27+
func NewDep() *dep {
28+
dep := new(dep)
29+
dep.edges = make(map[Node][]Node)
30+
return dep
31+
}
32+
33+
///////////////////////////////////////////////////////////////////////////////
34+
// STRINGIFY
35+
36+
func (d *dep) String() string {
37+
str := "dep{\n"
38+
for a, b := range d.edges {
39+
str += fmt.Sprint(" ", a) + " -> ["
40+
for i, c := range b {
41+
if i > 0 {
42+
str += ", "
43+
}
44+
str += fmt.Sprint(c)
45+
}
46+
str += "]\n"
47+
}
48+
str += "}"
49+
return str
50+
}
51+
52+
///////////////////////////////////////////////////////////////////////////////
53+
// PUBLIC METHODS
54+
55+
// Add a node a which depends on several other nodes b
56+
func (d *dep) AddNode(a Node, b ...Node) {
57+
if _, exists := d.edges[a]; exists {
58+
d.edges[a] = append(d.edges[a], b...)
59+
} else {
60+
d.edges[a] = b
61+
}
62+
for _, c := range b {
63+
if _, exists := d.edges[c]; !exists {
64+
d.edges[c] = []Node{}
65+
}
66+
}
67+
}
68+
69+
// Resolve returns the dependency order for a node
70+
func (d *dep) Resolve(n Node) ([]Node, error) {
71+
resolved, _, err := d.resolve(n, nil, nil)
72+
return resolved, err
73+
}
74+
75+
///////////////////////////////////////////////////////////////////////////////
76+
// PRIVATE METHODS
77+
78+
// Node a depends on several other nodes b
79+
func (d *dep) resolve(n Node, resolved []Node, unresolved []Node) ([]Node, []Node, error) {
80+
var err error
81+
82+
unresolved = append(unresolved, n)
83+
deps := d.edges[n]
84+
for _, c := range deps {
85+
if !slices.Contains(resolved, c) {
86+
if slices.Contains(unresolved, c) {
87+
return nil, nil, fmt.Errorf("circular dependency detected: %v -> %v", n, c)
88+
}
89+
resolved, unresolved, err = d.resolve(c, resolved, unresolved)
90+
if err != nil {
91+
return nil, nil, err
92+
}
93+
}
94+
}
95+
return append(resolved, n), remove(unresolved, n), nil
96+
}
97+
98+
// Remove a node from a list of nodes and return the new list
99+
func remove(nodes []Node, n Node) []Node {
100+
i := slices.Index(nodes, n)
101+
if i < 0 {
102+
return nodes
103+
}
104+
return append(nodes[:i], nodes[i+1:]...)
105+
}

pkg/provider/dep/dep_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package dep_test
2+
3+
import (
4+
"testing"
5+
6+
dep "github.com/mutablelogic/go-server/pkg/provider/dep"
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
type node struct {
11+
name string
12+
}
13+
14+
func (n node) String() string {
15+
return n.name
16+
}
17+
18+
func NewNode(name string) *node {
19+
return &node{name}
20+
}
21+
22+
func Test_dep_00(t *testing.T) {
23+
d := dep.NewDep()
24+
if d == nil {
25+
t.Fatalf("Expected a new dep, got nil")
26+
}
27+
28+
root := NewNode("root")
29+
a := NewNode("a")
30+
b := NewNode("b")
31+
c := NewNode("c")
32+
33+
// Add nodes a, b and c with dependencies
34+
d.AddNode(a, b, c)
35+
d.AddNode(root, a, b, c)
36+
37+
resolved, err := d.Resolve(root)
38+
if err != nil {
39+
t.Fatal(err)
40+
}
41+
t.Log(resolved)
42+
}
43+
44+
func Test_dep_01(t *testing.T) {
45+
assert := assert.New(t)
46+
d := dep.NewDep()
47+
if !assert.NotNil(d) {
48+
t.SkipNow()
49+
}
50+
51+
root := NewNode("root")
52+
a := NewNode("a")
53+
b := NewNode("b")
54+
c := NewNode("c")
55+
56+
// Add nodes a, b and c with dependencies
57+
d.AddNode(a, b, c)
58+
d.AddNode(root, a, b, c)
59+
d.AddNode(b, root)
60+
61+
_, err := d.Resolve(root)
62+
if !assert.NoError(err) {
63+
t.SkipNow()
64+
}
65+
}

0 commit comments

Comments
 (0)