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(examples): add p/fqname #2808

Merged
merged 2 commits into from
Sep 18, 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
72 changes: 72 additions & 0 deletions examples/gno.land/p/demo/fqname/fqname.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Package fqname provides utilities for handling fully qualified identifiers in
// Gno. A fully qualified identifier typically includes a package path followed
thehowl marked this conversation as resolved.
Show resolved Hide resolved
// by a dot (.) and then the name of a variable, function, type, or other
// package-level declaration.
package fqname

import "strings"

// Parse splits a fully qualified identifier into its package path and name
// components. It handles cases with and without slashes in the package path.
//
// pkgpath, name := fqname.Parse("gno.land/p/demo/avl.Tree")
// ufmt.Sprintf("Package: %s, Name: %s\n", id.Package, id.Name)
// // Output: Package: gno.land/p/demo/avl, Name: Tree
func Parse(fqname string) (pkgpath, name string) {
// Find the index of the last slash.
lastSlashIndex := strings.LastIndex(fqname, "/")
if lastSlashIndex == -1 {
// No slash found, handle it as a simple package name with dot notation.
dotIndex := strings.LastIndex(fqname, ".")
if dotIndex == -1 {
return fqname, ""
}
return fqname[:dotIndex], fqname[dotIndex+1:]
}

// Get the part after the last slash.
afterSlash := fqname[lastSlashIndex+1:]

// Check for a dot in the substring after the last slash.
dotIndex := strings.Index(afterSlash, ".")
if dotIndex == -1 {
// No dot found after the last slash
return fqname, ""
}

// Split at the dot to separate the base and the suffix.
base := fqname[:lastSlashIndex+1+dotIndex]
suffix := afterSlash[dotIndex+1:]

return base, suffix
}

// Construct a qualified identifier.
//
// fqName := fqname.Construct("gno.land/r/demo/foo20", "GRC20")
// fmt.Println("Fully Qualified Name:", fqName)
// // Output: gno.land/r/demo/foo20.GRC20
func Construct(pkgpath, name string) string {
// TODO: ensure pkgpath is valid - and as such last part does not contain a dot.
if name == "" {
thehowl marked this conversation as resolved.
Show resolved Hide resolved
return pkgpath
}
return pkgpath + "." + name
}

// RenderLink creates a formatted link for a fully qualified identifier.
// If the package path starts with "gno.land", it converts it to a markdown link.
// If the domain is different or missing, it returns the input as is.
func RenderLink(pkgPath, slug string) string {
if strings.HasPrefix(pkgPath, "gno.land") {
pkgLink := strings.TrimPrefix(pkgPath, "gno.land")
if slug != "" {
return "[" + pkgPath + "](" + pkgLink + ")." + slug
}
return "[" + pkgPath + "](" + pkgLink + ")"
}
if slug != "" {
return pkgPath + "." + slug
}
return pkgPath
}
74 changes: 74 additions & 0 deletions examples/gno.land/p/demo/fqname/fqname_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package fqname

import (
"testing"

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

func TestParse(t *testing.T) {
tests := []struct {
input string
expectedPkgPath string
expectedName string
}{
{"gno.land/p/demo/avl.Tree", "gno.land/p/demo/avl", "Tree"},
{"gno.land/p/demo/avl", "gno.land/p/demo/avl", ""},
{"gno.land/p/demo/avl.Tree.Node", "gno.land/p/demo/avl", "Tree.Node"},
{"gno.land/p/demo/avl/nested.Package.Func", "gno.land/p/demo/avl/nested", "Package.Func"},
{"path/filepath.Split", "path/filepath", "Split"},
{"path.Split", "path", "Split"},
{"path/filepath", "path/filepath", ""},
{"path", "path", ""},
{"", "", ""},
}

for _, tt := range tests {
pkgpath, name := Parse(tt.input)
uassert.Equal(t, tt.expectedPkgPath, pkgpath, "Package path did not match")
uassert.Equal(t, tt.expectedName, name, "Name did not match")
}
}

func TestConstruct(t *testing.T) {
tests := []struct {
pkgpath string
name string
expected string
}{
{"gno.land/r/demo/foo20", "GRC20", "gno.land/r/demo/foo20.GRC20"},
{"gno.land/r/demo/foo20", "", "gno.land/r/demo/foo20"},
{"path", "", "path"},
{"path", "Split", "path.Split"},
{"path/filepath", "", "path/filepath"},
{"path/filepath", "Split", "path/filepath.Split"},
{"", "JustName", ".JustName"},
{"", "", ""},
}

for _, tt := range tests {
result := Construct(tt.pkgpath, tt.name)
uassert.Equal(t, tt.expected, result, "Constructed FQName did not match expected")
}
}

func TestRenderLink(t *testing.T) {
tests := []struct {
pkgPath string
slug string
expected string
}{
{"gno.land/p/demo/avl", "Tree", "[gno.land/p/demo/avl](/p/demo/avl).Tree"},
{"gno.land/p/demo/avl", "", "[gno.land/p/demo/avl](/p/demo/avl)"},
{"github.com/a/b", "C", "github.com/a/b.C"},
{"example.com/pkg", "Func", "example.com/pkg.Func"},
{"gno.land/r/demo/foo20", "GRC20", "[gno.land/r/demo/foo20](/r/demo/foo20).GRC20"},
{"gno.land/r/demo/foo20", "", "[gno.land/r/demo/foo20](/r/demo/foo20)"},
{"", "", ""},
}

for _, tt := range tests {
result := RenderLink(tt.pkgPath, tt.slug)
uassert.Equal(t, tt.expected, result, "Rendered link did not match expected")
}
}
3 changes: 3 additions & 0 deletions examples/gno.land/p/demo/fqname/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module gno.land/p/demo/fqname

require gno.land/p/demo/uassert v0.0.0-latest
Loading