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
schema.gnogno
1package app23import (4	"errors"5	"strconv"6	"strings"7)89// A schema is what a handler says about itself: the verbs it answers, what10// each one takes, and what it gives back. Handlers declare one; the app reads11// it to enumerate the API, to validate payloads before dispatch, and to refuse12// an upgrade that would break a caller.13//14// This is the part a verb-dispatch API is usually missing. Without it, "the15// API" is whatever the live handler's switch statement happens to accept: not16// discoverable, not checkable, and free to change under callers without17// anything noticing. A schema makes it data, which is the same move that makes18// the API growable in the first place.19//20// What it is not is static typing. Gno has no reflection, so nothing here can21// look at your Go types. Validation happens at call time against declarations22// you wrote by hand, and a handler whose switch disagrees with its own schema23// is a bug the schema cannot catch.2425// Kind is the type of one payload field. The set is deliberately small: these26// are the types that survive a string encoding without ambiguity.27type Kind string2829const (30	KindString  Kind = "string"31	KindInt     Kind = "int"     // int64, base 1032	KindBool    Kind = "bool"    // "true" or "false"33	KindAddress Kind = "address" // bech32, validated34	KindNone    Kind = "none"    // a verb that returns nothing meaningful35)3637// Valid reports whether k is a known kind.38func (k Kind) Valid() bool {39	switch k {40	case KindString, KindInt, KindBool, KindAddress, KindNone:41		return true42	}43	return false44}4546// parse checks that v is a well-formed value of kind k.47func (k Kind) parse(v string) error {48	switch k {49	case KindString, KindNone:50		return nil51	case KindInt:52		if _, err := strconv.ParseInt(v, 10, 64); err != nil {53			return errors.New("expected an integer, got " + strconv.Quote(v))54		}55	case KindBool:56		if v != "true" && v != "false" {57			return errors.New("expected true or false, got " + strconv.Quote(v))58		}59	case KindAddress:60		if !address(v).IsValid() {61			return errors.New("expected an address, got " + strconv.Quote(v))62		}63	default:64		return errors.New("unknown kind " + string(k))65	}66	return nil67}6869// Field is one parameter of a verb.70type Field struct {71	Name string72	Kind Kind73	Doc  string74	Opt  bool // may be omitted, and only at the end of the parameter list75}7677// P declares a required parameter.78func P(name string, kind Kind, doc string) Field {79	return Field{Name: name, Kind: kind, Doc: doc}80}8182// Opt declares an optional parameter. Optional parameters must come last, and83// appending one is the only way to add a parameter to a verb without breaking84// callers.85func Opt(name string, kind Kind, doc string) Field {86	return Field{Name: name, Kind: kind, Doc: doc, Opt: true}87}8889// Verb is one operation's signature.90type Verb struct {91	Name   string92	Doc    string93	Result Kind94	Params []Field95}9697// Op declares a verb.98func Op(name, doc string, result Kind, params ...Field) Verb {99	return Verb{Name: name, Doc: doc, Result: result, Params: params}100}101102// Required returns how many leading parameters must be supplied.103func (v Verb) Required() int {104	n := 0105	for _, p := range v.Params {106		if p.Opt {107			break108		}109		n++110	}111	return n112}113114// Signature renders the verb as a one-line declaration:115//116//	add(text:string, urgent:bool?) -> int117func (v Verb) Signature() string {118	var b strings.Builder119	b.WriteString(v.Name)120	b.WriteString("(")121	for i, p := range v.Params {122		if i > 0 {123			b.WriteString(", ")124		}125		b.WriteString(p.Name + ":" + string(p.Kind))126		if p.Opt {127			b.WriteString("?")128		}129	}130	b.WriteString(") -> " + string(v.Result))131	return b.String()132}133134// Schema is a handler's whole API, sorted by verb name.135type Schema struct {136	verbs []Verb137}138139// NewSchema validates and sorts the declarations. It panics on a schema that140// could not be served correctly: a blank or duplicate name, an unknown kind, a141// required parameter after an optional one, or a duplicate parameter name.142// Those are all authoring mistakes, and a handler carrying one should fail to143// deploy rather than mis-describe itself forever.144func NewSchema(verbs ...Verb) *Schema {145	s := &Schema{verbs: make([]Verb, 0, len(verbs))}146	for _, v := range verbs {147		if strings.TrimSpace(v.Name) == "" || v.Name != strings.TrimSpace(v.Name) {148			panic(errors.New("app: verb names must be non-blank and unpadded"))149		}150		if !v.Result.Valid() {151			panic(errors.New("app: verb " + v.Name + ": unknown result kind " + string(v.Result)))152		}153		if _, dup := s.Lookup(v.Name); dup {154			panic(errors.New("app: duplicate verb " + v.Name))155		}156		seenOpt := false157		for i, p := range v.Params {158			if strings.TrimSpace(p.Name) == "" || p.Name != strings.TrimSpace(p.Name) {159				panic(errors.New("app: verb " + v.Name + ": parameter names must be non-blank and unpadded"))160			}161			if !p.Kind.Valid() || p.Kind == KindNone {162				panic(errors.New("app: verb " + v.Name + ": parameter " + p.Name +163					" has unusable kind " + string(p.Kind)))164			}165			if p.Opt {166				seenOpt = true167			} else if seenOpt {168				// The encoding is positional, so a required parameter after an169				// optional one could never be supplied without the optional.170				panic(errors.New("app: verb " + v.Name + ": required parameter " +171					p.Name + " follows an optional one"))172			}173			for j := 0; j < i; j++ {174				if v.Params[j].Name == p.Name {175					panic(errors.New("app: verb " + v.Name + ": duplicate parameter " + p.Name))176				}177			}178		}179		at := len(s.verbs)180		for i, e := range s.verbs {181			if v.Name < e.Name {182				at = i183				break184			}185		}186		s.verbs = append(s.verbs, Verb{})187		copy(s.verbs[at+1:], s.verbs[at:])188		s.verbs[at] = v189	}190	return s191}192193// Names returns the verb names, sorted.194func (s *Schema) Names() []string {195	if s == nil {196		return nil197	}198	out := make([]string, 0, len(s.verbs))199	for _, v := range s.verbs {200		out = append(out, v.Name)201	}202	return out203}204205// Lookup returns the verb by name.206func (s *Schema) Lookup(name string) (Verb, bool) {207	if s == nil {208		return Verb{}, false209	}210	for _, v := range s.verbs {211		if v.Name == name {212			return v, true213		}214	}215	return Verb{}, false216}217218// Verbs returns a copy of the declarations.219func (s *Schema) Verbs() []Verb {220	if s == nil {221		return nil222	}223	dup := make([]Verb, len(s.verbs))224	copy(dup, s.verbs)225	return dup226}227228// Signature renders one verb, or "" if it is not declared.229func (s *Schema) Signature(name string) string {230	if v, ok := s.Lookup(name); ok {231		return v.Signature()232	}233	return ""234}235236// Markdown renders the API as a table, for Render.237func (s *Schema) Markdown() string {238	if s == nil || len(s.verbs) == 0 {239		return "_No API declared._\n"240	}241	var b strings.Builder242	b.WriteString("| Verb | Payload | Returns | |\n|---|---|---|---|\n")243	for _, v := range s.verbs {244		params := make([]string, 0, len(v.Params))245		for _, p := range v.Params {246			f := p.Name + ":" + string(p.Kind)247			if p.Opt {248				f += "?"249			}250			params = append(params, f)251		}252		p := strings.Join(params, ", ")253		if p == "" {254			p = "—"255		}256		b.WriteString("| `" + v.Name + "` | `" + p + "` | `" + string(v.Result) +257			"` | " + v.Doc + " |\n")258	}259	return b.String()260}261262// JSON renders the schema for tooling: a client can build a form from this263// without reading any Gno source.264func (s *Schema) JSON() string {265	var b strings.Builder266	b.WriteString(`{"verbs":[`)267	if s != nil {268		for i, v := range s.verbs {269			if i > 0 {270				b.WriteString(",")271			}272			b.WriteString(`{"name":` + jsonStr(v.Name) +273				`,"doc":` + jsonStr(v.Doc) +274				`,"result":` + jsonStr(string(v.Result)) +275				`,"params":[`)276			for j, p := range v.Params {277				if j > 0 {278					b.WriteString(",")279				}280				b.WriteString(`{"name":` + jsonStr(p.Name) +281					`,"kind":` + jsonStr(string(p.Kind)) +282					`,"doc":` + jsonStr(p.Doc) +283					`,"optional":` + strconv.FormatBool(p.Opt) + `}`)284			}285			b.WriteString(`]}`)286		}287	}288	b.WriteString(`]}`)289	return b.String()290}291292// Diff reports what upgrading from prev to s would break for existing callers.293// An empty result means every payload that worked before still works.294//295// Breaking: a verb disappears, a parameter disappears or changes kind or296// position, a new required parameter appears, or a result kind changes.297// Not breaking: a new verb, a new optional parameter at the end, new298// documentation.299func (s *Schema) Diff(prev *Schema) []string {300	out := []string{}301	if prev == nil {302		return out303	}304	for _, old := range prev.verbs {305		nv, ok := s.Lookup(old.Name)306		if !ok {307			out = append(out, "verb "+old.Name+" removed")308			continue309		}310		if nv.Result != old.Result {311			out = append(out, "verb "+old.Name+": result changed from "+312				string(old.Result)+" to "+string(nv.Result))313		}314		for i, op := range old.Params {315			if i >= len(nv.Params) {316				out = append(out, "verb "+old.Name+": parameter "+op.Name+" removed")317				continue318			}319			np := nv.Params[i]320			if np.Name != op.Name {321				out = append(out, "verb "+old.Name+": parameter "+strconv.Itoa(i)+322					" renamed from "+op.Name+" to "+np.Name)323			}324			if np.Kind != op.Kind {325				out = append(out, "verb "+old.Name+": parameter "+op.Name+326					" changed from "+string(op.Kind)+" to "+string(np.Kind))327			}328			if np.Opt != op.Opt && !np.Opt {329				out = append(out, "verb "+old.Name+": parameter "+op.Name+330					" became required")331			}332		}333		for i := len(old.Params); i < len(nv.Params); i++ {334			if !nv.Params[i].Opt {335				out = append(out, "verb "+old.Name+": required parameter "+336					nv.Params[i].Name+" added")337			}338		}339	}340	return out341}342343// jsonStr quotes and escapes a string for the JSON rendering above.344func jsonStr(s string) string {345	var b strings.Builder346	b.WriteString(`"`)347	for _, r := range s {348		switch r {349		case '"':350			b.WriteString(`\"`)351		case '\\':352			b.WriteString(`\\`)353		case '\n':354			b.WriteString(`\n`)355		case '\r':356			b.WriteString(`\r`)357		case '\t':358			b.WriteString(`\t`)359		default:360			if r < 0x20 {361				b.WriteString(`\u00`)362				const hex = "0123456789abcdef"363				b.WriteByte(hex[(r>>4)&0xf])364				b.WriteByte(hex[r&0xf])365				continue366			}367			b.WriteRune(r)368		}369	}370	b.WriteString(`"`)371	return b.String()372}373

Functions

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

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