PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/p/g1lnkytfqcjwllws63gvf0mv9yt04aswy4y9amhm/app/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
g1lnkytfqcjwllws63gvf0mv9yt04aswy4y9amhm / app
Files
5 (gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/g1lnkytfqcjwllws63gvf0mv9yt04aswy4y9amhm/app/v0
gno
0.9

Files (5)

  • gnomod.tomltoml
  • app.gnogno
  • args.gnogno
  • schema.gnogno
  • store.gnogno
args.gnogno
1package app23import (4	"encoding/csv"5	"errors"6	"strconv"7	"strings"8	"unicode/utf8"9)1011// The wire format is one CSV record.12//13// CSV because it is in the standard library, because it quotes correctly --14// a task called `buy milk, eggs` survives a round trip, which a naive split15// on commas does not -- and because it is writable by hand on a gnokey16// command line. Fields are positional, in the order the verb declares them,17// which is why the schema forbids a required parameter after an optional one18// and why Diff treats a reordering as breaking.19//20//	Encode(addTask, "buy milk, eggs")  ->  "buy milk, eggs"   (quoted by csv)21//	Encode(transfer, "g1abc…", "42")   ->  "g1abc…,42"22//23// A verb with no parameters takes an empty payload.2425// Args is a decoded, validated payload. A handler receives one and reads26// fields by name; the app has already checked that every field is present and27// parses as its declared kind, so the accessors do not return errors.28type Args struct {29	verb   Verb30	values []string31}3233// Verb returns the name of the verb being called.34func (a *Args) Verb() string { return a.verb.Name }3536// Signature returns the declaration this payload was checked against, which37// is worth putting in a panic message.38func (a *Args) Signature() string { return a.verb.Signature() }3940// Raw returns the decoded fields in declaration order, for a handler that41// would rather loop than name them.42func (a *Args) Raw() []string {43	dup := make([]string, len(a.values))44	copy(dup, a.values)45	return dup46}4748// index returns the position of name and whether a value was supplied.49// It panics if the verb has no such parameter: that is the handler asking for50// something it did not declare, which no payload can fix.51func (a *Args) index(name string, want Kind) (int, bool) {52	for i, p := range a.verb.Params {53		if p.Name != name {54			continue55		}56		if want != "" && p.Kind != want {57			panic(errors.New("app: " + a.verb.Name + ": parameter " + name +58				" is declared " + string(p.Kind) + ", read as " + string(want)))59		}60		return i, i < len(a.values)61	}62	panic(errors.New("app: " + a.verb.Name + " has no parameter " + name +63		"; its signature is " + a.verb.Signature()))64}6566// Has reports whether an optional parameter was supplied.67func (a *Args) Has(name string) bool {68	_, ok := a.index(name, "")69	return ok70}7172// String returns a string parameter, or "" if it was optional and omitted.73func (a *Args) String(name string) string {74	i, ok := a.index(name, KindString)75	if !ok {76		return ""77	}78	return a.values[i]79}8081// Int returns an int parameter, or 0 if it was optional and omitted.82func (a *Args) Int(name string) int64 {83	i, ok := a.index(name, KindInt)84	if !ok {85		return 086	}87	n, _ := strconv.ParseInt(a.values[i], 10, 64) // checked at decode88	return n89}9091// Bool returns a bool parameter, or false if it was optional and omitted.92func (a *Args) Bool(name string) bool {93	i, ok := a.index(name, KindBool)94	if !ok {95		return false96	}97	return a.values[i] == "true"98}99100// Address returns an address parameter, or the zero address if it was101// optional and omitted.102func (a *Args) Address(name string) address {103	i, ok := a.index(name, KindAddress)104	if !ok {105		return ""106	}107	return address(a.values[i])108}109110// Encode builds a payload for v from values given in declaration order,111// validating each against its declared kind. Use it when one realm calls112// another app, so an encoding mistake fails where it was made.113func Encode(v Verb, values ...string) string {114	if err := checkArity(v, len(values)); err != nil {115		panic(err)116	}117	for i, val := range values {118		if err := v.Params[i].Kind.parse(val); err != nil {119			panic(fieldErr(v, i, err))120		}121	}122	if len(values) == 0 {123		return ""124	}125	// A lone empty field encodes to nothing at all -- csv does not quote an126	// empty field, so the record "" and the record with no fields are the127	// same bytes. Quote it explicitly so the payload says which it is.128	if len(values) == 1 && values[0] == "" {129		return `""`130	}131	// Fast path: when nothing needs quoting, a CSV record IS the fields132	// joined by commas, and building a csv.Writer to discover that costs133	// more than the whole rest of the call. See needsQuotes.134	plain := true135	for _, val := range values {136		if needsQuotes(val) {137			plain = false138			break139		}140	}141	if plain {142		return strings.Join(values, ",")143	}144145	var b strings.Builder146	w := csv.NewWriter(&b)147	if err := w.Write(values); err != nil {148		panic(errors.New("app: " + v.Name + ": " + err.Error()))149	}150	w.Flush()151	if err := w.Error(); err != nil {152		panic(errors.New("app: " + v.Name + ": " + err.Error()))153	}154	return strings.TrimRight(b.String(), "\n")155}156157// splitPlain splits a payload on commas in a single pass, reporting false the158// moment it meets a byte that gives commas a different meaning. The one pass159// is the point: bailing out and splitting separately doubles the interpreted160// work over the whole payload.161func splitPlain(payload string) ([]string, bool) {162	out := []string{}163	start := 0164	for i := 0; i < len(payload); i++ {165		switch payload[i] {166		case '"', '\r', '\n':167			return nil, false168		case ',':169			out = append(out, payload[start:i])170			start = i + 1171		}172	}173	return append(out, payload[start:]), true174}175176// needsQuotes mirrors csv.Writer's rule exactly. Diverging from it would mean177// Encode produced something csv.Reader reads back differently, so this is178// copied rather than approximated: a field is quoted when it is the literal179// `\.`, when it contains a comma, quote, CR or LF, or when it begins with a180// space.181func needsQuotes(field string) bool {182	if field == "" {183		return false184	}185	if field == `\.` {186		return true187	}188	switch field[0] {189	case ' ', '\t', '\v', '\f':190		return true191	}192	// A leading non-ASCII space (U+00A0 and friends) is rare enough to hand193	// to the slow path rather than decode a rune for on every call.194	if field[0] >= utf8.RuneSelf {195		return true196	}197	// One pass, for the same reason splitPlain is one pass:198	// strings.ContainsAny is interpreted Gno and costs more over a short199	// field than the csv.Writer it is trying to avoid.200	for i := 0; i < len(field); i++ {201		switch field[i] {202		case ',', '"', '\r', '\n':203			return true204		}205	}206	return false207}208209// checkArity reports whether n values can satisfy v.210func checkArity(v Verb, n int) error {211	req := v.Required()212	if n < req {213		return errors.New("app: " + v.Name + " needs at least " + strconv.Itoa(req) +214			" parameter(s), got " + strconv.Itoa(n) + "; signature is " + v.Signature())215	}216	if n > len(v.Params) {217		return errors.New("app: " + v.Name + " takes at most " + strconv.Itoa(len(v.Params)) +218			" parameter(s), got " + strconv.Itoa(n) + "; signature is " + v.Signature())219	}220	return nil221}222223// fieldErr names the verb, the parameter and the signature, which is what a224// caller needs to fix the payload without reading the handler's source.225func fieldErr(v Verb, i int, err error) error {226	return errors.New("app: " + v.Name + ": parameter " + v.Params[i].Name +227		": " + err.Error() + "; signature is " + v.Signature())228}229230// decode parses and validates a payload against v.231func decode(v Verb, payload string) (*Args, error) {232	values := []string{}233	if payload == "" {234		// Ambiguous on its own: no fields, or one empty field. The verb235		// decides. A required first parameter means an empty payload is that236		// parameter, empty -- which is what a gnokey caller passing "" for a237		// string means, and Encode spells `""` for the same case.238		if v.Required() >= 1 {239			values = []string{""}240		}241	} else if rec, plain := splitPlain(payload); plain {242		// Fast path: with no quote and no newline anywhere, no field is243		// quoted, so every comma is a separator and every field is literal244		// -- exactly what csv.Reader would return. splitPlain decides that245		// and does the split in one pass, because every string operation246		// here is interpreted Gno: two passes with strings.ContainsAny and247		// strings.Split costs more than the csv parse it replaces.248		values = rec249	} else {250		rec, err := csv.NewReader(strings.NewReader(payload)).Read()251		if err != nil {252			return nil, errors.New("app: " + v.Name + ": malformed payload: " + err.Error())253		}254		values = rec255	}256	if err := checkArity(v, len(values)); err != nil {257		return nil, err258	}259	for i, val := range values {260		if err := v.Params[i].Kind.parse(val); err != nil {261			return nil, fieldErr(v, i, err)262		}263	}264	return &Args{verb: v, values: values}, nil265}266

Functions

not supported for pure packages by the node (vm/qfuncs)

Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.