Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

toposortdemo.gno

3.40 Kb · 115 lines
  1// Package toposortdemo is a small gnoweb demo of the dependency ordering
  2// provided by the [p/moul/x/daily/toposort](/p/moul/x/daily/toposort/v0)
  3// library: it shows a build graph resolved into a safe install order, and a
  4// deliberately broken graph to show how a cycle is reported.
  5//
  6// It contains no ordering logic of its own — the graph, the sort and the cycle
  7// detection all come from the library. Stateless and read-only, so Render is
  8// fully deterministic.
  9package toposortdemo
 10
 11import (
 12	"strconv"
 13	"strings"
 14
 15	"gno.land/p/moul/x/daily/toposort/v0"
 16)
 17
 18// buildGraph is a small, realistic dependency graph: a realm on top of a couple
 19// of libraries, themselves on a shared core.
 20func buildGraph() *toposort.Graph {
 21	return toposort.FromPairs([][2]string{
 22		{"realm", "ui"},
 23		{"realm", "storage"},
 24		{"ui", "markdown"},
 25		{"storage", "avl"},
 26		{"markdown", "strings"},
 27		{"avl", "strings"},
 28	})
 29}
 30
 31// cyclicGraph is broken on purpose: auth and session need each other.
 32func cyclicGraph() *toposort.Graph {
 33	return toposort.FromPairs([][2]string{
 34		{"server", "auth"},
 35		{"auth", "session"},
 36		{"session", "auth"},
 37	})
 38}
 39
 40// Render renders the demo for gnoweb.
 41//
 42//	Render("")       / Render("/") -> the build graph, resolved
 43//	Render("/cycle")               -> the broken graph and its report
 44func Render(path string) string {
 45	var b strings.Builder
 46	b.WriteString("# Topological Sort\n\n")
 47	b.WriteString("Ordering a dependency graph so nothing is built before what it needs, ")
 48	b.WriteString("demoing the [`p/moul/x/daily/toposort`](/p/moul/x/daily/toposort/v0) library.\n\n")
 49
 50	if parseArg(path) == "cycle" {
 51		return b.String() + renderCycle()
 52	}
 53
 54	g := buildGraph()
 55	b.WriteString("## The graph\n\n```\n")
 56	b.WriteString(g.String())
 57	b.WriteString("```\n\n")
 58
 59	order, err := g.Sort()
 60	if err != nil {
 61		b.WriteString("_Unexpectedly cyclic._\n")
 62		return b.String()
 63	}
 64	b.WriteString("## Install order\n\n")
 65	for i, n := range order {
 66		b.WriteString(strconv.Itoa(i + 1))
 67		b.WriteString(". `")
 68		b.WriteString(n)
 69		b.WriteString("`\n")
 70	}
 71	b.WriteString("\nTies are broken lexicographically, so this ordering is the ")
 72	b.WriteString("*only* one the library will ever produce for this graph.\n\n")
 73	b.WriteString("> See [`/cycle`](/r/moul/x/daily/toposortdemo/v0:cycle) for what happens ")
 74	b.WriteString("when the graph is not a DAG.\n")
 75	return b.String()
 76}
 77
 78// renderCycle shows the failure mode.
 79func renderCycle() string {
 80	g := cyclicGraph()
 81	var b strings.Builder
 82	b.WriteString("## A graph with a cycle\n\n```\n")
 83	b.WriteString(g.String())
 84	b.WriteString("```\n\n")
 85
 86	order, err := g.Sort()
 87	if err == nil {
 88		b.WriteString("_Unexpectedly acyclic._\n")
 89		return b.String()
 90	}
 91	b.WriteString("`Sort` returns an error: **")
 92	b.WriteString(err.Error())
 93	b.WriteString("**\n\n")
 94	b.WriteString("Ordered before giving up: ")
 95	if len(order) == 0 {
 96		b.WriteString("_nothing_")
 97	} else {
 98		b.WriteString("`" + strings.Join(order, "`, `") + "`")
 99	}
100	b.WriteString("\n\nStuck on the cycle: `")
101	b.WriteString(strings.Join(g.CycleNodes(), "`, `"))
102	b.WriteString("`\n\n> The nodes still stuck are named rather than silently dropped, ")
103	b.WriteString("so a caller can report exactly which dependencies are tangled.\n")
104	return b.String()
105}
106
107// parseArg extracts the first path segment.
108func parseArg(path string) string {
109	s := strings.TrimSpace(path)
110	s = strings.TrimPrefix(s, "/")
111	if i := strings.IndexByte(s, '/'); i >= 0 {
112		s = s[:i]
113	}
114	return s
115}