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

render.gno

2.97 Kb · 123 lines
  1package home
  2
  3import (
  4	"strconv"
  5	"strings"
  6
  7	"chain/runtime"
  8)
  9
 10const (
 11	realmPath = "gno.land/r/samcrew/home"
 12	webPath   = "/r/samcrew/home"
 13)
 14
 15const fallbackLayout = "# samcrew\n\n:intro:\n\n## The crew\n\n:crew:\n"
 16
 17// Render serves:
 18//
 19//	""             the team page
 20//	"slots"        the slot index
 21//	"slots/<slug>" one slot's raw markdown
 22func Render(path string) string {
 23	switch {
 24	case path == "":
 25		return renderPage()
 26	case path == "slots":
 27		return renderIndex()
 28	case strings.HasPrefix(path, "slots/"):
 29		return renderSlot(strings.TrimPrefix(path, "slots/"))
 30	}
 31	// The path is not echoed: it comes from the visitor's URL.
 32	return "# Not found\n\nThis page does not exist.\n\n[Back home](" + webPath + ")\n"
 33}
 34
 35func renderPage() string {
 36	layout := Get(layoutSlug)
 37	if layout == "" {
 38		layout = fallbackLayout
 39	}
 40	return fill(layout)
 41}
 42
 43// fill replaces every :slug: in s, in one left-to-right pass. Replacement text
 44// is never rescanned, and an unknown placeholder stays verbatim.
 45func fill(s string) string {
 46	var b strings.Builder
 47	for {
 48		i := strings.IndexByte(s, ':')
 49		if i < 0 {
 50			b.WriteString(s)
 51			return b.String()
 52		}
 53		b.WriteString(s[:i])
 54		s = s[i:]
 55		j := strings.IndexByte(s[1:], ':')
 56		if j < 0 {
 57			b.WriteString(s)
 58			return b.String()
 59		}
 60		if v, ok := lookup(s[1 : j+1]); ok {
 61			b.WriteString(v)
 62			s = s[j+2:]
 63			continue
 64		}
 65		b.WriteString(":")
 66		s = s[1:]
 67	}
 68}
 69
 70func lookup(slug string) (string, bool) {
 71	if !validSlug(slug) {
 72		return "", false
 73	}
 74	switch slug {
 75	case "chainid":
 76		return runtime.ChainID(), true
 77	case "crew":
 78		return crewTable(), true
 79	case "hero":
 80		return heroImage(), true
 81	case "height":
 82		return strconv.FormatInt(runtime.ChainHeight(), 10), true
 83	case "realm":
 84		return realmPath, true
 85	case "rev":
 86		return strconv.Itoa(rev), true
 87	}
 88	if strings.HasPrefix(slug, galleryPrefix) {
 89		return renderGallery(strings.TrimPrefix(slug, galleryPrefix))
 90	}
 91	if v := slots.Get(slug); v != nil {
 92		return v.(*slot).body, true
 93	}
 94	return "", false
 95}
 96
 97func renderIndex() string {
 98	var b strings.Builder
 99	b.WriteString("# Slots\n\nrev " + strconv.Itoa(rev) + " · " + strconv.Itoa(slots.Size()) + " slot(s) · admin " + admin.String() + "\n\n")
100	b.WriteString("| slot | bytes | rev | height |\n| --- | ---: | ---: | ---: |\n")
101	slots.Iterate("", "", func(key string, value any) bool {
102		s := value.(*slot)
103		b.WriteString("| [" + key + "](" + webPath + ":slots/" + key + ") | " +
104			strconv.Itoa(len(s.body)) + " | " + strconv.Itoa(s.rev) + " | " +
105			strconv.FormatInt(s.updated, 10) + " |\n")
106		return false
107	})
108	return b.String()
109}
110
111func renderSlot(slug string) string {
112	v := slots.Get(slug)
113	if v == nil {
114		return "# Not found\n\nNo such slot.\n\n[Slot index](" + webPath + ":slots)\n"
115	}
116	s := v.(*slot)
117	body := s.body
118	if !strings.HasSuffix(body, "\n") {
119		body += "\n"
120	}
121	return "# " + slug + "\n\n" + strconv.Itoa(len(s.body)) + " bytes · rev " + strconv.Itoa(s.rev) +
122		" · written at height " + strconv.FormatInt(s.updated, 10) + "\n\n````\n" + body + "````\n"
123}