Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add p/moul/txlink + p/moul/helplink #2887

Merged
merged 10 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions examples/gno.land/p/moul/helplink/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module gno.land/p/moul/helplink

require (
gno.land/p/demo/urequire v0.0.0-latest
gno.land/p/moul/txlink v0.0.0-latest
)
79 changes: 79 additions & 0 deletions examples/gno.land/p/moul/helplink/helplink.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Package helplink provides utilities for creating help page links compatible
// with Gnoweb, Gnobro, and other clients that support the Gno contracts'
// flavored Markdown format.
//
// This package simplifies the generation of dynamic, context-sensitive help
// links, enabling users to navigate relevant documentation seamlessly within
// the Gno ecosystem.
//
// For a more lightweight alternative, consider using p/moul/txlink.
//
// The primary functions — Func, FuncURL, and Home — are intended for use with
// the "relative realm". When specifying a custom Realm, you can create links
// that utilize either the current realm path or a fully qualified path to
// another realm.
package helplink

import (
"strings"

"gno.land/p/moul/txlink"
)

const chainDomain = "gno.land" // XXX: std.ChainDomain (#2911)

// Func returns a markdown link for the specific function with optional
// key-value arguments, for the current realm.
func Func(title string, fn string, args ...string) string {
return Realm("").Func(title, fn, args...)
}

// FuncURL returns a URL for the specified function with optional key-value
// arguments, for the current realm.
func FuncURL(fn string, args ...string) string {
return Realm("").FuncURL(fn, args...)
}

// Home returns the URL for the help homepage of the current realm.
func Home() string {
return Realm("").Home()
}

// Realm represents a specific realm for generating help links.
type Realm string

// prefix returns the URL prefix for the realm.
func (r Realm) prefix() string {
// relative
if r == "" {
return ""
}

// local realm -> /realm
realm := string(r)
if strings.Contains(realm, chainDomain) {
return strings.TrimPrefix(realm, chainDomain)
}

// remote realm -> https://remote.land/realm
return "https://" + string(r)
}

// Func returns a markdown link for the specified function with optional
// key-value arguments.
func (r Realm) Func(title string, fn string, args ...string) string {
// XXX: escape title
return "[" + title + "](" + r.FuncURL(fn, args...) + ")"
}

// FuncURL returns a URL for the specified function with optional key-value
// arguments.
func (r Realm) FuncURL(fn string, args ...string) string {
tlr := txlink.Realm(r)
return tlr.URL(fn, args...)
}

// Home returns the base help URL for the specified realm.
func (r Realm) Home() string {
return r.prefix() + "?help"
}
78 changes: 78 additions & 0 deletions examples/gno.land/p/moul/helplink/helplink_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package helplink

import (
"testing"

"gno.land/p/demo/urequire"
)

func TestFunc(t *testing.T) {
tests := []struct {
title string
fn string
args []string
want string
realm Realm
}{
{"Example", "foo", []string{"bar", "1", "baz", "2"}, "[Example](?help&__func=foo&bar=1&baz=2)", ""},
{"Realm Example", "foo", []string{"bar", "1", "baz", "2"}, "[Realm Example](/r/lorem/ipsum?help&__func=foo&bar=1&baz=2)", "gno.land/r/lorem/ipsum"},
{"Single Arg", "testFunc", []string{"key", "value"}, "[Single Arg](?help&__func=testFunc&key=value)", ""},
{"No Args", "noArgsFunc", []string{}, "[No Args](?help&__func=noArgsFunc)", ""},
{"Odd Args", "oddArgsFunc", []string{"key"}, "[Odd Args](?help&__func=oddArgsFunc)", ""},
}

for _, tt := range tests {
t.Run(tt.title, func(t *testing.T) {
got := tt.realm.Func(tt.title, tt.fn, tt.args...)
urequire.Equal(t, tt.want, got)
})
}
}

func TestFuncURL(t *testing.T) {
tests := []struct {
fn string
args []string
want string
realm Realm
}{
{"foo", []string{"bar", "1", "baz", "2"}, "?help&__func=foo&bar=1&baz=2", ""},
{"testFunc", []string{"key", "value"}, "?help&__func=testFunc&key=value", ""},
{"noArgsFunc", []string{}, "?help&__func=noArgsFunc", ""},
{"oddArgsFunc", []string{"key"}, "?help&__func=oddArgsFunc", ""},
{"foo", []string{"bar", "1", "baz", "2"}, "/r/lorem/ipsum?help&__func=foo&bar=1&baz=2", "gno.land/r/lorem/ipsum"},
{"testFunc", []string{"key", "value"}, "/r/lorem/ipsum?help&__func=testFunc&key=value", "gno.land/r/lorem/ipsum"},
{"noArgsFunc", []string{}, "/r/lorem/ipsum?help&__func=noArgsFunc", "gno.land/r/lorem/ipsum"},
{"oddArgsFunc", []string{"key"}, "/r/lorem/ipsum?help&__func=oddArgsFunc", "gno.land/r/lorem/ipsum"},
{"foo", []string{"bar", "1", "baz", "2"}, "https://gno.world/r/lorem/ipsum?help&__func=foo&bar=1&baz=2", "gno.world/r/lorem/ipsum"},
{"testFunc", []string{"key", "value"}, "https://gno.world/r/lorem/ipsum?help&__func=testFunc&key=value", "gno.world/r/lorem/ipsum"},
{"noArgsFunc", []string{}, "https://gno.world/r/lorem/ipsum?help&__func=noArgsFunc", "gno.world/r/lorem/ipsum"},
{"oddArgsFunc", []string{"key"}, "https://gno.world/r/lorem/ipsum?help&__func=oddArgsFunc", "gno.world/r/lorem/ipsum"},
}

for _, tt := range tests {
title := tt.fn
t.Run(title, func(t *testing.T) {
got := tt.realm.FuncURL(tt.fn, tt.args...)
urequire.Equal(t, tt.want, got)
})
}
}

func TestHome(t *testing.T) {
tests := []struct {
realm Realm
want string
}{
{"", "?help"},
{"gno.land/r/lorem/ipsum", "/r/lorem/ipsum?help"},
{"gno.world/r/lorem/ipsum", "https://gno.world/r/lorem/ipsum?help"},
}

for _, tt := range tests {
t.Run(string(tt.realm), func(t *testing.T) {
got := tt.realm.Home()
urequire.Equal(t, tt.want, got)
})
}
}
3 changes: 3 additions & 0 deletions examples/gno.land/p/moul/txlink/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module gno.land/p/moul/txlink

require gno.land/p/demo/urequire v0.0.0-latest
74 changes: 74 additions & 0 deletions examples/gno.land/p/moul/txlink/txlink.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Package txlink provides utilities for creating transaction-related links
// compatible with Gnoweb, Gnobro, and other clients within the Gno ecosystem.
//
// This package is optimized for generating lightweight transaction links with
// flexible arguments, allowing users to build dynamic links that integrate
// seamlessly with various Gno clients.
//
// The primary function, URL, is designed to produce markdown links for
// transaction functions in the current "relative realm". By specifying a custom
// Realm, you can generate links that either use the current realm path or a
// fully qualified path for another realm.
//
// This package is a streamlined alternative to helplink, providing similar
// functionality for transaction links without the full feature set of helplink.
package txlink

import (
"std"
"strings"
)

const chainDomain = "gno.land" // XXX: std.ChainDomain (#2911)

// URL returns a URL for the specified function with optional key-value
// arguments, for the current realm.
func URL(fn string, args ...string) string {
return Realm("").URL(fn, args...)
}

// Realm represents a specific realm for generating tx links.
type Realm string

// prefix returns the URL prefix for the realm.
func (r Realm) prefix() string {
// relative
if r == "" {
curPath := std.CurrentRealm().PkgPath()
return strings.TrimPrefix(curPath, chainDomain)
}

// local realm -> /realm
realm := string(r)
if strings.Contains(realm, chainDomain) {
return strings.TrimPrefix(realm, chainDomain)
}

// remote realm -> https://remote.land/realm
return "https://" + string(r)
}

// URL returns a URL for the specified function with optional key-value
// arguments.
func (r Realm) URL(fn string, args ...string) string {
// Start with the base query
url := r.prefix() + "?help&__func=" + fn

// Check if args length is even
if len(args)%2 != 0 {
// If not even, we can choose to handle the error here.
// For example, we can just return the URL without appending
// more args.
return url
}

// Append key-value pairs to the URL
for i := 0; i < len(args); i += 2 {
key := args[i]
value := args[i+1]
// XXX: escape keys and args
url += "&" + key + "=" + value
}

return url
}
37 changes: 37 additions & 0 deletions examples/gno.land/p/moul/txlink/txlink_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package txlink

import (
"testing"

"gno.land/p/demo/urequire"
)

func TestURL(t *testing.T) {
tests := []struct {
fn string
args []string
want string
realm Realm
}{
{"foo", []string{"bar", "1", "baz", "2"}, "?help&__func=foo&bar=1&baz=2", ""},
{"testFunc", []string{"key", "value"}, "?help&__func=testFunc&key=value", ""},
{"noArgsFunc", []string{}, "?help&__func=noArgsFunc", ""},
{"oddArgsFunc", []string{"key"}, "?help&__func=oddArgsFunc", ""},
{"foo", []string{"bar", "1", "baz", "2"}, "/r/lorem/ipsum?help&__func=foo&bar=1&baz=2", "gno.land/r/lorem/ipsum"},
{"testFunc", []string{"key", "value"}, "/r/lorem/ipsum?help&__func=testFunc&key=value", "gno.land/r/lorem/ipsum"},
{"noArgsFunc", []string{}, "/r/lorem/ipsum?help&__func=noArgsFunc", "gno.land/r/lorem/ipsum"},
{"oddArgsFunc", []string{"key"}, "/r/lorem/ipsum?help&__func=oddArgsFunc", "gno.land/r/lorem/ipsum"},
{"foo", []string{"bar", "1", "baz", "2"}, "https://gno.world/r/lorem/ipsum?help&__func=foo&bar=1&baz=2", "gno.world/r/lorem/ipsum"},
{"testFunc", []string{"key", "value"}, "https://gno.world/r/lorem/ipsum?help&__func=testFunc&key=value", "gno.world/r/lorem/ipsum"},
{"noArgsFunc", []string{}, "https://gno.world/r/lorem/ipsum?help&__func=noArgsFunc", "gno.world/r/lorem/ipsum"},
{"oddArgsFunc", []string{"key"}, "https://gno.world/r/lorem/ipsum?help&__func=oddArgsFunc", "gno.world/r/lorem/ipsum"},
}

for _, tt := range tests {
title := tt.fn
t.Run(title, func(t *testing.T) {
got := tt.realm.URL(tt.fn, tt.args...)
urequire.Equal(t, tt.want, got)
})
}
}
5 changes: 2 additions & 3 deletions examples/gno.land/r/demo/boards/board.gno
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

"gno.land/p/demo/avl"
"gno.land/p/moul/txlink"
)

//----------------------------------------
Expand Down Expand Up @@ -134,7 +135,5 @@ func (board *Board) GetURLFromThreadAndReplyID(threadID, replyID PostID) string
}

func (board *Board) GetPostFormURL() string {
return "/r/demo/boards?help&__func=CreateThread" +
"&bid=" + board.id.String() +
"&body.type=textarea"
return txlink.URL("CreateThread", "bid", board.id.String())
}
1 change: 1 addition & 0 deletions examples/gno.land/r/demo/boards/gno.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ module gno.land/r/demo/boards

require (
gno.land/p/demo/avl v0.0.0-latest
gno.land/p/moul/txlink v0.0.0-latest
gno.land/r/demo/users v0.0.0-latest
)
30 changes: 15 additions & 15 deletions examples/gno.land/r/demo/boards/post.gno
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

"gno.land/p/demo/avl"
"gno.land/p/moul/txlink"
)

//----------------------------------------
Expand Down Expand Up @@ -155,27 +156,26 @@ func (post *Post) GetURL() string {
}

func (post *Post) GetReplyFormURL() string {
return "/r/demo/boards?help&__func=CreateReply" +
"&bid=" + post.board.id.String() +
"&threadid=" + post.threadID.String() +
"&postid=" + post.id.String() +
"&body.type=textarea"
return txlink.URL("CreateReply",
"bid", post.board.id.String(),
"threadid", post.threadID.String(),
"postid", post.id.String(),
)
}

func (post *Post) GetRepostFormURL() string {
return "/r/demo/boards?help&__func=CreateRepost" +
"&bid=" + post.board.id.String() +
"&postid=" + post.id.String() +
"&title.type=textarea" +
"&body.type=textarea" +
"&dstBoardID.type=textarea"
return txlink.URL("CreateRepost",
"bid", post.board.id.String(),
"postid", post.id.String(),
)
}

func (post *Post) GetDeleteFormURL() string {
return "/r/demo/boards?help&__func=DeletePost" +
"&bid=" + post.board.id.String() +
"&threadid=" + post.threadID.String() +
"&postid=" + post.id.String()
return txlink.URL("DeletePost",
"bid", post.board.id.String(),
"threadid", post.threadID.String(),
"postid", post.id.String(),
)
}

func (post *Post) RenderSummary() string {
Expand Down
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/boards/z_0_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func main() {
}

// Output:
// \[[post](/r/demo/boards?help&__func=CreateThread&bid=1&body.type=textarea)]
// \[[post](/r/demo/boards?help&__func=CreateThread&bid=1)]
//
// ----------------------------------------
// ## [First Post (title)](/r/demo/boards:test_board/1)
Expand Down
6 changes: 3 additions & 3 deletions examples/gno.land/r/demo/boards/z_10_c_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ func main() {
// # First Post in (title)
//
// Body of the first post. (body)
// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1&body.type=textarea)] \[[repost](/r/demo/boards?help&__func=CreateRepost&bid=1&postid=1&title.type=textarea&body.type=textarea&dstBoardID.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)]
// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1)] \[[repost](/r/demo/boards?help&__func=CreateRepost&bid=1&postid=1)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)]
//
// > First reply of the First post
// >
// > \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1/2) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=2&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=2)]
// > \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1/2) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=2)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=2)]
//
// ----------------------------------------------------
// # First Post in (title)
//
// Body of the first post. (body)
// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1&body.type=textarea)] \[[repost](/r/demo/boards?help&__func=CreateRepost&bid=1&postid=1&title.type=textarea&body.type=textarea&dstBoardID.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)]
// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1)] \[[repost](/r/demo/boards?help&__func=CreateRepost&bid=1&postid=1)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)]
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/boards/z_10_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func main() {
// # First Post in (title)
//
// Body of the first post. (body)
// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1&body.type=textarea)] \[[repost](/r/demo/boards?help&__func=CreateRepost&bid=1&postid=1&title.type=textarea&body.type=textarea&dstBoardID.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)]
// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1)] \[[repost](/r/demo/boards?help&__func=CreateRepost&bid=1&postid=1)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)]
//
// ----------------------------------------------------
// thread does not exist with id: 1
Loading
Loading