Skip to content

Commit

Permalink
feat(examples): add p/fqname (#2808)
Browse files Browse the repository at this point in the history
Extracted from #2551 (also #2516).

<!-- please provide a detailed description of the changes made in this
pull request. -->

<details><summary>Contributors' checklist...</summary>

- [ ] Added new tests, or not needed, or not feasible
- [ ] Provided an example (e.g. screenshot) to aid review or the PR is
self-explanatory
- [ ] Updated the official documentation or not needed
- [ ] No breaking changes were made, or a `BREAKING CHANGE: xxx` message
was included in the description
- [ ] Added references to related issues and PRs
- [ ] Provided any useful hints for running manual tests
- [ ] Added new benchmarks to [generated
graphs](https://gnoland.github.io/benchmarks), if any. More info
[here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
</details>

---------

Signed-off-by: moul <[email protected]>
Co-authored-by: Morgan <[email protected]>
  • Loading branch information
moul and thehowl committed Sep 18, 2024
1 parent 6f3a094 commit 01ee5a9
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 0 deletions.
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
// 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 == "" {
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

0 comments on commit 01ee5a9

Please sign in to comment.