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

cowsay.gno

3.46 Kb · 147 lines
  1// Package cowsay is an on-chain Gno port of the classic `cowsay` program:
  2// it prints an ASCII cow with a speech bubble containing a message.
  3//
  4// Original: cowsay by Tony Monroe (1999), a Perl program later ported to Go
  5// many times. This realm reimplements the core rendering — bubble framing,
  6// word-wrapping and the cow art — as pure, deterministic string logic.
  7package cowsay
  8
  9import (
 10	"strings"
 11)
 12
 13// defaultMessage is shown when no message is supplied.
 14const defaultMessage = "Moo!"
 15
 16// wrapWidth is the maximum bubble text width (classic cowsay uses 40).
 17const wrapWidth = 40
 18
 19// Say returns the full cowsay art (speech bubble + cow) for msg.
 20// An empty msg falls back to the default message.
 21func Say(msg string) string {
 22	msg = strings.TrimSpace(msg)
 23	if msg == "" {
 24		msg = defaultMessage
 25	}
 26	lines := wrap(msg, wrapWidth)
 27	return balloon(lines) + cow
 28}
 29
 30// balloon builds the speech bubble around the given (already wrapped) lines.
 31func balloon(lines []string) string {
 32	width := maxLen(lines)
 33
 34	var b strings.Builder
 35	// top border: " " + "_"*(width+2)
 36	b.WriteString(" ")
 37	b.WriteString(strings.Repeat("_", width+2))
 38	b.WriteString("\n")
 39
 40	n := len(lines)
 41	for i, ln := range lines {
 42		left, right := borderChars(i, n)
 43		b.WriteString(left)
 44		b.WriteString(" ")
 45		b.WriteString(ln)
 46		b.WriteString(strings.Repeat(" ", width-len(ln)))
 47		b.WriteString(" ")
 48		b.WriteString(right)
 49		b.WriteString("\n")
 50	}
 51
 52	// bottom border: " " + "-"*(width+2)
 53	b.WriteString(" ")
 54	b.WriteString(strings.Repeat("-", width+2))
 55	b.WriteString("\n")
 56	return b.String()
 57}
 58
 59// borderChars picks the left/right bubble characters for line i of n.
 60// Single line uses < >. Multi-line uses / \ for the first row,
 61// \ / for the last row, and | | for the middle rows.
 62func borderChars(i, n int) (string, string) {
 63	if n == 1 {
 64		return "<", ">"
 65	}
 66	switch i {
 67	case 0:
 68		return "/", "\\"
 69	case n - 1:
 70		return "\\", "/"
 71	default:
 72		return "|", "|"
 73	}
 74}
 75
 76// wrap splits text into lines no longer than width, breaking on spaces.
 77// Words longer than width are hard-split.
 78func wrap(text string, width int) []string {
 79	words := strings.Fields(text)
 80	if len(words) == 0 {
 81		return []string{""}
 82	}
 83
 84	var lines []string
 85	cur := ""
 86	for _, w := range words {
 87		// hard-split a single word that is too long
 88		for len(w) > width {
 89			if cur != "" {
 90				lines = append(lines, cur)
 91				cur = ""
 92			}
 93			lines = append(lines, w[:width])
 94			w = w[width:]
 95		}
 96		if cur == "" {
 97			cur = w
 98		} else if len(cur)+1+len(w) <= width {
 99			cur = cur + " " + w
100		} else {
101			lines = append(lines, cur)
102			cur = w
103		}
104	}
105	if cur != "" {
106		lines = append(lines, cur)
107	}
108	return lines
109}
110
111// maxLen returns the length of the longest line.
112func maxLen(lines []string) int {
113	m := 0
114	for _, ln := range lines {
115		if len(ln) > m {
116			m = len(ln)
117		}
118	}
119	return m
120}
121
122// cow is the classic happy cow, aligned under the bubble.
123const cow = `        \   ^__^
124         \  (oo)\_______
125            (__)\       )\/\
126                ||----w |
127                ||     ||
128`
129
130// Render displays the cow for gnoweb.
131//
132//	Render("")            -> default message ("Moo!")
133//	Render("/hello there") -> renders "hello there", word-wrapped
134func Render(path string) string {
135	msg := strings.TrimLeft(path, "/")
136	art := Say(msg)
137
138	var b strings.Builder
139	b.WriteString("# cowsay\n\n")
140	if strings.TrimSpace(msg) == "" {
141		b.WriteString("_Tip: pass a message in the path, e.g._ `/Hello, gno.land!`\n\n")
142	}
143	b.WriteString("```\n")
144	b.WriteString(art)
145	b.WriteString("```\n")
146	return b.String()
147}