Search Apps Documentation Source Content File Folder Download Copy Actions Download 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}