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

romannumdemo.gno

3.60 Kb · 110 lines
  1// Package romannumdemo is a small gnoweb demo of the Roman-numeral converter
  2// provided by the [p/moul/x/daily/romannum](/p/moul/x/daily/romannum/v0)
  3// library: it renders a table of worked examples and interactive
  4// integer→Roman and Roman→integer conversions.
  5//
  6// It contains no conversion logic of its own — everything comes from
  7// romannum.ToRoman and romannum.FromRoman.
  8package romannumdemo
  9
 10import (
 11	"strconv"
 12	"strings"
 13
 14	"gno.land/p/moul/x/daily/romannum/v0"
 15)
 16
 17// examples are the classic milestones shown by the root Render.
 18var examples = []int{1, 4, 9, 14, 40, 49, 90, 400, 900, 1994, 2024, 3888, 3999}
 19
 20// Render drives gnoweb:
 21//   - ""        or "/"          → a table of example conversions
 22//   - "/<n>"                    → integer → Roman
 23//   - "/r/<roman>"              → Roman → integer
 24func Render(path string) string {
 25	arg := strings.Trim(strings.TrimSpace(path), "/")
 26
 27	switch {
 28	case arg == "":
 29		return renderTable()
 30	case strings.HasPrefix(strings.ToLower(arg), "r/"):
 31		return renderFromRoman(arg[2:])
 32	default:
 33		return renderToRoman(arg)
 34	}
 35}
 36
 37func renderToRoman(arg string) string {
 38	n, err := strconv.Atoi(strings.TrimSpace(arg))
 39	if err != nil {
 40		return "# Roman numeral converter\n\nCould not parse `" + arg +
 41			"` as an integer.\n\n" + usage()
 42	}
 43	if n < 1 || n > 3999 {
 44		return "# Roman numeral converter\n\n`" + strconv.Itoa(n) +
 45			"` is out of range — Roman numerals here cover **1..3999**.\n\n" + usage()
 46	}
 47	var b strings.Builder
 48	b.WriteString("# " + strconv.Itoa(n) + " → " + romannum.ToRoman(n) + "\n\n")
 49	b.WriteString("| decimal | roman |\n|---:|:---|\n")
 50	b.WriteString("| " + strconv.Itoa(n) + " | **" + romannum.ToRoman(n) + "** |\n\n")
 51	if n > 1 {
 52		b.WriteString("[« " + strconv.Itoa(n-1) + "](/r/moul/x/daily/romannumdemo/v0:" +
 53			strconv.Itoa(n-1) + ") · ")
 54	}
 55	if n < 3999 {
 56		b.WriteString("[" + strconv.Itoa(n+1) + " »](/r/moul/x/daily/romannumdemo/v0:" +
 57			strconv.Itoa(n+1) + ")")
 58	}
 59	b.WriteString("\n\n" + usage())
 60	return b.String()
 61}
 62
 63func renderFromRoman(arg string) string {
 64	arg = strings.TrimSpace(arg)
 65	n, ok := tryFromRoman(arg)
 66	if !ok {
 67		return "# Roman numeral converter\n\n`" + arg +
 68			"` is not a valid Roman numeral (1..3999).\n\n" + usage()
 69	}
 70	up := strings.ToUpper(arg)
 71	var b strings.Builder
 72	b.WriteString("# " + up + " → " + strconv.Itoa(n) + "\n\n")
 73	b.WriteString("| roman | decimal |\n|:---|---:|\n")
 74	b.WriteString("| **" + up + "** | " + strconv.Itoa(n) + " |\n\n")
 75	b.WriteString(usage())
 76	return b.String()
 77}
 78
 79// tryFromRoman wraps the panic-on-invalid romannum.FromRoman in a panic-free
 80// (value, ok) shape, so the demo can report a friendly error for bad input.
 81func tryFromRoman(s string) (n int, ok bool) {
 82	defer func() {
 83		if recover() != nil {
 84			n, ok = 0, false
 85		}
 86	}()
 87	return romannum.FromRoman(s), true
 88}
 89
 90func renderTable() string {
 91	var b strings.Builder
 92	b.WriteString("# Roman numeral converter\n\n")
 93	b.WriteString("A tiny demo of the [`p/moul/x/daily/romannum`](/p/moul/x/daily/romannum/v0) ")
 94	b.WriteString("library — the classic `ToRoman` / `FromRoman` kata. ")
 95	b.WriteString("Pure and deterministic.\n\n")
 96	b.WriteString("## Examples\n\n")
 97	b.WriteString("| decimal | roman |\n|---:|:---|\n")
 98	for _, n := range examples {
 99		b.WriteString("| [" + strconv.Itoa(n) + "](/r/moul/x/daily/romannumdemo/v0:" +
100			strconv.Itoa(n) + ") | " + romannum.ToRoman(n) + " |\n")
101	}
102	b.WriteString("\n" + usage())
103	return b.String()
104}
105
106func usage() string {
107	return "## Try it\n\n" +
108		"- `:/<n>` — integer → Roman, e.g. `:/2024` → " + romannum.ToRoman(2024) + "\n" +
109		"- `:/r/<roman>` — Roman → integer, e.g. `:/r/MMXXIV` → 2024\n"
110}