PathrockNetwork Gno Explorer
HomeBlocksTransactionsRealmsPackagesValidatorsAnalytics

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/r/moul/x/daily/urlshort/v0

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v0
Namespace
moul / x / daily / urlshort
Files
3 (README)(gnomod.toml)
Exported functions
4
Module
gno.land/r/moul/x/daily/urlshort/v0
gno
0.9

Files (3)

  • README.mdmarkdown
  • gnomod.tomltoml
  • urlshort.gnogno
urlshort.gnogno
1// Package urlshort is a simple on-chain URL alias registry.2//3// Callers register an `alias -> url` mapping they own. An alias can only be4// claimed once; the owner may later update the target URL, but nobody else can5// overwrite an alias they do not own. A per-alias click counter is bumped every6// time the alias is resolved through Render("/<alias>").7package urlshort89import (10	"strings"1112	"chain"13	"chain/runtime/unsafe"1415	"gno.land/p/nt/avl/v0"16)1718// entry is the persisted record for a single alias.19type entry struct {20	url    string21	owner  address22	note   string23	clicks int24}2526// aliases maps alias (string) -> *entry, kept in an avl.Tree for deterministic27// iteration order in Render.28var aliases avl.Tree2930// isAlphanumeric reports whether s is non-empty and made only of [0-9A-Za-z].31func isAlphanumeric(s string) bool {32	if s == "" {33		return false34	}35	for _, c := range s {36		switch {37		case c >= '0' && c <= '9':38		case c >= 'a' && c <= 'z':39		case c >= 'A' && c <= 'Z':40		default:41			return false42		}43	}44	return true45}4647// Shorten registers `alias -> url` owned by the caller.48//49// Rules:50//   - alias must be non-empty and alphanumeric;51//   - url must be non-empty;52//   - if the alias is unclaimed, it is created owned by the caller;53//   - if it is already claimed by the caller, the url/note are updated;54//   - if it is claimed by someone else, the call aborts.55func Shorten(cur realm, alias string, url string, note string) {56	if !isAlphanumeric(alias) {57		panic("alias must be non-empty and alphanumeric")58	}59	if url == "" {60		panic("url must be non-empty")61	}6263	caller := unsafe.PreviousRealm().Address()6465	if v := aliases.Get(alias); v != nil {66		e := v.(*entry)67		if e.owner != caller {68			panic("alias already taken by another owner")69		}70		e.url = url71		e.note = note72		chain.Emit("AliasUpdated", "alias", alias, "owner", caller.String())73		return74	}7576	e := &entry{url: url, owner: caller, note: note, clicks: 0}77	aliases.Set(alias, e)78	chain.Emit("AliasCreated", "alias", alias, "owner", caller.String())79}8081// Remove deletes an alias owned by the caller. Aborts if the alias does not82// exist or the caller is not the owner.83func Remove(cur realm, alias string) {84	v := aliases.Get(alias)85	if v == nil {86		panic("alias not found")87	}88	e := v.(*entry)89	caller := unsafe.PreviousRealm().Address()90	if e.owner != caller {91		panic("only the owner can remove an alias")92	}93	aliases.Remove(alias)94	chain.Emit("AliasRemoved", "alias", alias, "owner", caller.String())95}9697// Lookup returns the target url for an alias and whether it exists. It is a98// read-only helper and does NOT increment the click counter.99func Lookup(alias string) (string, bool) {100	v := aliases.Get(alias)101	if v == nil {102		return "", false103	}104	return v.(*entry).url, true105}106107// Render renders Markdown. The root path lists every alias; "/<alias>" shows a108// single alias detail and bumps its click counter.109func Render(path string) string {110	if path == "" || path == "/" {111		return renderIndex()112	}113114	alias := strings.TrimPrefix(path, "/")115	v := aliases.Get(alias)116	if v == nil {117		return "# URL Shortener\n\nNo alias named `" + alias + "`.\n\n[← all aliases](/)\n"118	}119120	e := v.(*entry)121	e.clicks++122123	var b strings.Builder124	b.WriteString("# 🔗 " + alias + "\n\n")125	b.WriteString("**Target:** <" + e.url + ">\n\n")126	b.WriteString("**Owner:** " + e.owner.String() + "\n\n")127	if e.note != "" {128		b.WriteString("**Note:** " + e.note + "\n\n")129	}130	b.WriteString("**Clicks:** " + itoa(e.clicks) + "\n\n")131	b.WriteString("[← all aliases](/)\n")132	return b.String()133}134135func renderIndex() string {136	var b strings.Builder137	b.WriteString("# 🔗 URL Shortener\n\n")138139	if aliases.Size() == 0 {140		b.WriteString("_No aliases registered yet._\n\n")141		b.WriteString("Call `Shorten(alias, url, note)` to register one.\n")142		return b.String()143	}144145	b.WriteString("| Alias | Target | Owner | Clicks |\n")146	b.WriteString("|-------|--------|-------|--------|\n")147	aliases.Iterate("", "", func(key string, value interface{}) bool {148		e := value.(*entry)149		b.WriteString("| [" + key + "](/" + key + ") | <" + e.url + "> | " +150			short(e.owner.String()) + " | " + itoa(e.clicks) + " |\n")151		return false152	})153	b.WriteString("\nTotal aliases: " + itoa(aliases.Size()) + "\n")154	return b.String()155}156157// short truncates a long address for the index table.158func short(s string) string {159	if len(s) <= 12 {160		return s161	}162	return s[:6] + "…" + s[len(s)-4:]163}164165// itoa converts a non-negative int to its decimal string without importing166// strconv (kept minimal & deterministic).167func itoa(n int) string {168	if n == 0 {169		return "0"170	}171	neg := n < 0172	if neg {173		n = -n174	}175	var buf [20]byte176	i := len(buf)177	for n > 0 {178		i--179		buf[i] = byte('0' + n%10)180		n /= 10181	}182	if neg {183		i--184		buf[i] = '-'185	}186	return string(buf[i:])187}188

Functions

  • Lookup(alias string) (string, bool)

  • Remove(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, alias string)

  • Render(path string) string

  • Shorten(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, alias string, url string, note string)

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

Rendered

RenderedRawgnoweb ↗

vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.