ringlog.gno
2.63 Kb · 110 lines
1// Package ringlog is an on-chain port of Go's container/ring: a fixed-capacity
2// circular message board where new posts overwrite the oldest entries once full.
3package ringlog
4
5import (
6 "chain/runtime"
7 "chain/runtime/unsafe"
8 "strconv"
9 "strings"
10)
11
12// Cap is the maximum number of messages the ring buffer holds.
13const Cap = 16
14
15// Entry is a single posted message.
16type Entry struct {
17 Author address
18 Message string
19 Height int64
20}
21
22var (
23 buf [Cap]Entry // the ring storage
24 head int // index of the next write slot
25 count int // number of entries currently stored (0 ≤ count ≤ Cap)
26)
27
28// Post adds a message to the ring buffer.
29// Once Cap entries are stored, the oldest entry is overwritten.
30func Post(msg string) {
31 msg = strings.TrimSpace(msg)
32 if len(msg) == 0 {
33 panic("message cannot be empty")
34 }
35 if len(msg) > 280 {
36 panic("message too long (max 280 chars)")
37 }
38 buf[head] = Entry{
39 Author: unsafe.PreviousRealm().Address(),
40 Message: msg,
41 Height: runtime.ChainHeight(),
42 }
43 head = (head + 1) % Cap
44 if count < Cap {
45 count++
46 }
47}
48
49// Entries returns all stored messages in chronological order (oldest first).
50func Entries() []Entry {
51 if count == 0 {
52 return nil
53 }
54 result := make([]Entry, count)
55 oldest := (head - count + Cap) % Cap
56 for i := 0; i < count; i++ {
57 result[i] = buf[(oldest+i)%Cap]
58 }
59 return result
60}
61
62// Len returns the number of messages currently stored.
63func Len() int { return count }
64
65// Render displays the ring buffer as a Markdown page.
66func Render(path string) string {
67 var sb strings.Builder
68
69 sb.WriteString("# RingLog\n\n")
70 sb.WriteString("> A ring-buffer message board (capacity: **")
71 sb.WriteString(strconv.Itoa(Cap))
72 sb.WriteString("**). New messages overwrite the oldest once the buffer is full.\n\n")
73
74 if count == 0 {
75 sb.WriteString("*No messages yet. Call `Post(msg)` to add one.*\n")
76 return sb.String()
77 }
78
79 sb.WriteString("| # | Author | Block | Message |\n")
80 sb.WriteString("|---|--------|-------|---------|\n")
81
82 entries := Entries()
83 for i, e := range entries {
84 sb.WriteString("| ")
85 sb.WriteString(strconv.Itoa(i + 1))
86 sb.WriteString(" | `")
87 sb.WriteString(shorten(e.Author.String()))
88 sb.WriteString("` | ")
89 sb.WriteString(strconv.FormatInt(e.Height, 10))
90 sb.WriteString(" | ")
91 sb.WriteString(e.Message)
92 sb.WriteString(" |\n")
93 }
94
95 sb.WriteString("\n*Showing ")
96 sb.WriteString(strconv.Itoa(count))
97 sb.WriteString(" of ")
98 sb.WriteString(strconv.Itoa(Cap))
99 sb.WriteString(" slots used.*\n")
100
101 return sb.String()
102}
103
104// shorten trims a long address for display.
105func shorten(s string) string {
106 if len(s) <= 14 {
107 return s
108 }
109 return s[:6] + "…" + s[len(s)-6:]
110}