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

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}