text.gno
3.26 Kb · 136 lines
1// Package text holds the small string helpers shared by the GnoRadio realms.
2package text
3
4import (
5 "strconv"
6 "strings"
7 "unicode"
8)
9
10// Str quotes a value as a JSON string without escaping. Use it only for values
11// whose validator already refuses `"`, `\` and control characters (Valid text,
12// validated URLs, hashes, addresses, constants): escaping costs about 1M gas
13// per field in the VM.
14func Str(s string) string { return `"` + s + `"` }
15
16// Valid accepts min..max runes of letters (any script), digits, spaces and
17// . , ' - ! ? : / &. Markdown, HTML and JSON specials are refused, so the text
18// is safe in markdown and JSON as is (SVG still escapes '&'). Fullwidth and
19// mathematical letters (which dodge word filters) and the blank Hangul fillers
20// are refused too.
21func Valid(s string, min, max int) bool {
22 n := 0
23 for _, r := range s {
24 n++
25 if r >= 0xFF01 && r <= 0xFF5E || r >= 0x1D400 && r <= 0x1D7FF || r == 0x115F || r == 0x1160 || r == 0x3164 || r == 0xFFA0 {
26 return false
27 }
28 if unicode.IsLetter(r) || unicode.IsDigit(r) {
29 continue
30 }
31 switch r {
32 case ' ', '.', ',', '\'', '-', '!', '?', ':', '/', '&':
33 continue
34 }
35 return false
36 }
37 return n >= min && n <= max
38}
39
40// Digits reports whether s is a non-empty run of ASCII digits.
41func Digits(s string) bool {
42 if s == "" {
43 return false
44 }
45 for _, c := range s {
46 if c < '0' || c > '9' {
47 return false
48 }
49 }
50 return true
51}
52
53// GNOT formats ugnot as GNOT with up to 6 decimals.
54func GNOT(u int64) string {
55 const ugnot = 1_000_000
56 whole := strconv.FormatInt(u/ugnot, 10)
57 frac := u % ugnot
58 if frac == 0 {
59 return whole + " GNOT"
60 }
61 f := strconv.FormatInt(frac, 10)
62 f = strings.TrimRight(strings.Repeat("0", 6-len(f))+f, "0")
63 return whole + "." + f + " GNOT"
64}
65
66// Itoa and Itoa64 skip strconv for the frequent zero: every conversion
67// costs gas.
68func Itoa(n int) string {
69 if n == 0 {
70 return "0"
71 }
72 return strconv.Itoa(n)
73}
74
75func Itoa64(n int64) string {
76 if n == 0 {
77 return "0"
78 }
79 return strconv.FormatInt(n, 10)
80}
81
82// Atoi and Atoi64 read a stored decimal; anything else is 0.
83func Atoi(s string) int {
84 n, _ := strconv.Atoi(s)
85 return n
86}
87
88func Atoi64(s string) int64 {
89 n, _ := strconv.ParseInt(s, 10, 64)
90 return n
91}
92
93// MustAtoi and MustAtoi64 read a stored number on a money or authority path:
94// a corrupt record panics (the transaction reverts) instead of reading as 0.
95func MustAtoi(s string) int {
96 n, err := strconv.Atoi(s)
97 if err != nil {
98 panic("text: not a number: " + s)
99 }
100 return n
101}
102
103func MustAtoi64(s string) int64 {
104 n, err := strconv.ParseInt(s, 10, 64)
105 if err != nil {
106 panic("text: not a number: " + s)
107 }
108 return n
109}
110
111// Flag is a stored boolean: "1" or "".
112func Flag(b bool) string {
113 if b {
114 return "1"
115 }
116 return ""
117}
118
119// URLChars accepts only URL-safe characters (A-Z a-z 0-9 - . _ ~ : / ? # =
120// & % + @): safe in markdown link destinations and JSON as is.
121func URLChars(s string) bool {
122 for _, c := range s {
123 switch {
124 case c >= 'a' && c <= 'z', c >= 'A' && c <= 'Z', c >= '0' && c <= '9':
125 case strings.ContainsRune("-._~:/?#=&%+@", c):
126 default:
127 return false
128 }
129 }
130 return true
131}
132
133// HTTPS accepts an https link of URL-safe characters, 11 to max bytes.
134func HTTPS(s string, max int) bool {
135 return strings.HasPrefix(s, "https://") && len(s) > 10 && len(s) <= max && URLChars(s)
136}