Skip to content

Commit

Permalink
fix: route tree fix
Browse files Browse the repository at this point in the history
  • Loading branch information
morlay committed Sep 26, 2024
1 parent 4c7d38a commit b615ae6
Show file tree
Hide file tree
Showing 4 changed files with 250 additions and 151 deletions.
47 changes: 47 additions & 0 deletions internal/pathpattern/ordered_map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package pathpattern

import "iter"

type orderedMap[K comparable, V any] struct {
m map[K]V
list []V
}

func (m *orderedMap[K, V]) Add(k K, v V) {
if m.m == nil {
m.m = map[K]V{}
}
if _, ok := m.m[k]; !ok {
m.m[k] = v
m.list = append(m.list, v)
}
}

func (m *orderedMap[K, V]) Keys() iter.Seq[K] {
return func(yield func(K) bool) {
for k := range m.m {
if !yield(k) {
return
}
}
}
}

func (m *orderedMap[K, V]) Values() iter.Seq[V] {
return func(yield func(V) bool) {
for _, item := range m.list {
if !yield(item) {
return
}
}
}
}

func (m *orderedMap[K, V]) Len() int {
return len(m.m)
}

func (m *orderedMap[K, V]) Get(k K) (V, bool) {
v, ok := m.m[k]
return v, ok
}
114 changes: 51 additions & 63 deletions internal/pathpattern/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package pathpattern
import (
"fmt"
"io"
"iter"
"sort"
"strings"
)
Expand All @@ -27,74 +28,66 @@ func (t *Tree[N]) String() string {
}

type group[N Node] struct {
seg Segment
parent *group[N]
childExactly map[Segment]*group[N]
childWild *group[N]
nodes map[string]*N
seg Segment
parent *group[N]
childWild *group[N]

childExactly orderedMap[Segment, *group[N]]

nodes orderedMap[string, *N]
}

type Route struct {
Method string
PathSegments Segments
ChildSegments []Segment
Method string
PathSegments Segments
}

func (r *Route) String() string {
b := &strings.Builder{}
b.WriteString(r.PathSegments.String())

if len(r.ChildSegments) > 0 {
b.WriteString("/(")
for i, c := range r.ChildSegments {
if i > 0 {
b.WriteString("|")
}
b.WriteString(c.String())
}
b.WriteString(")")
}

return b.String()
}

func (g *group[N]) EachRoute(each func(n N, parents []*Route), parents ...*Route) {
for _, node := range g.nodes {
each(*node, parents)
}
func (g *group[N]) Route(parents ...*Route) iter.Seq2[N, []*Route] {
return func(yield func(N, []*Route) bool) {
for node := range g.nodes.Values() {
if !(yield(*node, parents)) {
return
}
}

var route *Route
var route *Route

if named, ok := g.seg.(NamedSegment); ok && named.Multiple() {
route = &Route{
PathSegments: g.PathSegments(),
if named, ok := g.seg.(NamedSegment); ok && named.Multiple() {
route = &Route{
PathSegments: g.PathSegments(),
}
} else if g.childExactly.Len() > 0 && g.childWild != nil {
route = &Route{
PathSegments: g.PathSegments(),
}
}
} else if len(g.childExactly) > 0 && g.childWild != nil {
route = &Route{
PathSegments: g.PathSegments(),

if route != nil {
parents = append(parents, route)
}
}

if route != nil {
for _, c := range g.childExactly {
route.ChildSegments = append(route.ChildSegments, c.seg)
for c := range g.childExactly.Values() {
for node, pp := range c.Route(parents...) {
if !(yield(node, pp)) {
return
}
}
}

if c := g.childWild; c != nil {
route.ChildSegments = append(route.ChildSegments, c.seg)
for node, pp := range c.Route(parents...) {
if !(yield(node, pp)) {
return
}
}
}

parents = append(parents, route)
}

for _, c := range g.childExactly {
c.EachRoute(each, parents...)
}

if c := g.childWild; c != nil {
c.EachRoute(each, parents...)
}

}

func (g *group[N]) PathSegments() Segments {
Expand All @@ -109,10 +102,7 @@ func (g *group[N]) PathSegments() Segments {

func (g *group[N]) add(method string, segs Segments, node N) {
if len(segs) == 0 {
if g.nodes == nil {
g.nodes = map[string]*N{}
}
g.nodes[method] = &node
g.nodes.Add(method, &node)
return
}

Expand All @@ -138,17 +128,13 @@ func (g *group[N]) add(method string, segs Segments, node N) {
return
}

if g.childExactly == nil {
g.childExactly = map[Segment]*group[N]{}
}

child, ok := g.childExactly[seg]
child, ok := g.childExactly.Get(seg)
if !ok {
child = &group[N]{
seg: seg,
parent: g,
}
g.childExactly[seg] = child
g.childExactly.Add(seg, child)
}

child.add(method, segs[1:], node)
Expand All @@ -163,8 +149,8 @@ func (g *group[N]) PrintTo(w io.Writer, level int) {
_, _ = fmt.Fprintf(w, g.seg.String())
}

if len(g.nodes) > 0 {
for _, node := range g.nodes {
if g.nodes.Len() > 0 {
for node := range g.nodes.Values() {
_, _ = fmt.Fprintf(w, "\n")
if level > 0 {
_, _ = fmt.Fprintf(w, strings.Repeat(" ", level+1))
Expand All @@ -177,16 +163,18 @@ func (g *group[N]) PrintTo(w io.Writer, level int) {
_, _ = fmt.Fprintf(w, "\n")
}

if g.childExactly != nil {
exactlySegments := make([]Segment, 0, len(g.childExactly))
for s := range g.childExactly {
if n := g.childExactly.Len(); n > 0 {
exactlySegments := make([]Segment, 0, n)
for s := range g.childExactly.Keys() {
exactlySegments = append(exactlySegments, s)
}
sort.Slice(exactlySegments, func(i, j int) bool {
return exactlySegments[i].String() < exactlySegments[j].String()
})

for _, seg := range exactlySegments {
g.childExactly[seg].PrintTo(w, level+1)
v, _ := g.childExactly.Get(seg)
v.PrintTo(w, level+1)
}
}

Expand Down
16 changes: 8 additions & 8 deletions internal/pathpattern/tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ import (
func TestTree(t *testing.T) {
tree := &Tree[*operation]{}

tree.Add(Path(http.MethodGet, lit("v0"), lit("xxx")))
tree.Add(Path(http.MethodPost, lit("v0"), lit("xxx")))
tree.Add(Path(http.MethodGet, lit("v0"), lit("store"), namedMulti("scope"), lit("blobs"), lit("uploads")))
tree.Add(Path(http.MethodGet, lit("v0"), lit("store"), namedMulti("scope"), lit("blobs"), named("digest")))
tree.Add(Path(http.MethodGet, lit("v0"), lit("store"), namedMulti("scope"), lit("manifests"), named("reference")))
tree.Add(createPath(http.MethodGet, lit("v0"), lit("xxx")))
tree.Add(createPath(http.MethodPost, lit("v0"), lit("xxx")))
tree.Add(createPath(http.MethodGet, lit("v0"), lit("store"), namedMulti("scope"), lit("blobs"), lit("uploads")))
tree.Add(createPath(http.MethodGet, lit("v0"), lit("store"), namedMulti("scope"), lit("blobs"), named("digest")))
tree.Add(createPath(http.MethodGet, lit("v0"), lit("store"), namedMulti("scope"), lit("manifests"), named("reference")))

tree.EachRoute(func(n *operation, parents []*Route) {
for n, parents := range tree.Route() {
fmt.Println(n.Method(), n.PathSegments(), parents)
})
}
}

func Path(m string, segments ...Segment) *operation {
func createPath(m string, segments ...Segment) *operation {
return &operation{method: m, segments: segments}
}

Expand Down
Loading

0 comments on commit b615ae6

Please sign in to comment.