Search Apps Documentation Source Content File Folder Download Copy Actions Download

validation.gno

2.59 Kb · 99 lines
 1package user
 2
 3import (
 4	"regexp"
 5	"strconv"
 6	"strings"
 7)
 8
 9// Validation helpers in this file are for value-level checks at public API boundaries.
10//
11// Add helpers here when they validate a parameter or payload value without
12// depending on mutable contract state or call flow. Good examples are address
13// format, nickname format, string-map shape, allowed key shape, and page/count limits.
14//
15// Do not add helpers here when the assertion depends on store contents,
16// ownership, permissions, freeze/migration state, uniqueness, or the order of a
17// specific operation. Keep those assertions close to the domain flow that owns
18// the state transition.
19//
20// As a rule of thumb: validation.gno owns "is this value shaped correctly?";
21// domain files own "is this action allowed right now?".
22
23var reURLLikeNickname = regexp.MustCompile(`(?i)([a-z][a-z0-9+.-]*://|www\.|([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,})`)
24
25// Address assertions.
26// Used by user public entry points and batch address query payloads.
27func assertIsValidAddress(addr address) {
28	if !addr.IsValid() {
29		panic("invalid address: " + addr.String())
30	}
31}
32
33func assertValidAddresses(addrs []address) {
34	for _, addr := range addrs {
35		assertIsValidAddress(addr)
36	}
37}
38
39// Nickname assertions.
40// Used by create/update/query entry points before touching nickname indexes.
41func assertValidNickname(nickname string) {
42	if len(nickname) == 0 {
43		panic("nickname cannot be empty")
44	}
45
46	if len(nickname) > 20 {
47		panic("nickname too long (max 20)")
48	}
49
50	trimmed := strings.TrimSpace(nickname)
51	if len(trimmed) == 0 {
52		panic("nickname cannot be only whitespace")
53	}
54
55	if nickname != trimmed {
56		panic("nickname cannot have leading or trailing whitespace")
57	}
58
59	if isURLLikeNickname(nickname) {
60		panic("nickname must not be a URL")
61	}
62
63	if hasControlChar(nickname) {
64		panic("nickname must not contain control characters")
65	}
66}
67
68func isURLLikeNickname(nickname string) bool {
69	return reURLLikeNickname.MatchString(strings.TrimSpace(nickname))
70}
71
72// hasControlChar rejects ASCII control bytes, including DEL (0x7f).
73func hasControlChar(s string) bool {
74	for i := 0; i < len(s); i++ {
75		c := s[i]
76		if c < 0x20 || c == 0x7f {
77			return true
78		}
79	}
80	return false
81}
82
83// Query limit assertions.
84// Used by public list and batch query entry points.
85func assertListLimit(field string, count int) {
86	if count > listLimit {
87		panic(field + " exceeds listLimit (max: " + strconv.Itoa(listLimit) + ")")
88	}
89}
90
91func assertListPageCount(page int, count int) {
92	if page < 1 {
93		panic("page must be at least 1")
94	}
95	if count < 1 {
96		panic("count must be at least 1")
97	}
98	assertListLimit("count", count)
99}