impl.gno
1.69 Kb · 66 lines
1// Package impl is version 0 of the handler realm of the "API as data" upgrade
2// pattern (pattern G; see ../../README.md).
3//
4// Two verbs, declared as text rather than as a method set. Deploying nominates
5// it; the facade owner accepts it by path.
6package impl
7
8import (
9 "strings"
10
11 facade "gno.land/r/moul/x/upgrade/schema/facade/v0"
12)
13
14// schema is this version's whole API. One verb per line, name then parameters.
15const schema = `upper s
16repeat s n`
17
18type handler struct{}
19
20func (handler) Schema() string { return schema }
21
22func (handler) Invoke(verb string, args []string) string {
23 switch verb {
24 case "upper":
25 return strings.ToUpper(args[0])
26 case "repeat":
27 return strings.Repeat(args[0], atoi(args[1]))
28 }
29 // Unreachable: the facade checked the verb against this schema first.
30 panic("unknown verb " + verb)
31}
32
33// atoi is hand-rolled to keep the failure message about the ARGUMENT rather
34// than about strconv, which is the whole difference a typed boundary would have
35// made here.
36func atoi(s string) int {
37 if s == "" {
38 panic("expected a number, got an empty argument")
39 }
40 n := 0
41 for _, c := range s {
42 if c < '0' || c > '9' {
43 panic("expected a number, got " + s)
44 }
45 n = n*10 + int(c-'0')
46 if n > 64 {
47 panic("refusing to repeat more than 64 times")
48 }
49 }
50 return n
51}
52
53// Instance exposes the singleton.
54func Instance() facade.Handler { return handler{} }
55
56// Path is this realm's own package path, the string the facade owner accepts.
57const Path = "gno.land/r/moul/x/upgrade/schema/impl/v0"
58
59func init(cur realm) {
60 facade.Propose(cross(cur), Instance())
61}
62
63// Propose re-nominates this handler, so a rollback needs no redeploy.
64func Propose(cur realm) {
65 facade.Propose(cross(cur), Instance())
66}