1// Package realmpath is a lightweight Render.path parsing and link generation2// library with an idiomatic API, closely resembling that of net/url.3//4// This package provides utilities for parsing request paths and query5// parameters, allowing you to extract path segments and manipulate query6// values.7//8// Example usage:9//10// import "gno.land/p/moul/realmpath/v0"11//12// func Render(path string) string {13// // Parsing a sample path with query parameters14// path = "hello/world?foo=bar&baz=foobar"15// req := realmpath.Parse(path)16//17// // Accessing parsed path and query parameters18// println(req.Path) // Output: hello/world19// println(req.PathPart(0)) // Output: hello20// println(req.PathPart(1)) // Output: world21// println(req.Query.Get("foo")) // Output: bar22// println(req.Query.Get("baz")) // Output: foobar23//24// // Rebuilding the URL25// println(req.String()) // Output: /r/current/realm:hello/world?baz=foobar&foo=bar26// }27package realmpath2829import (30 "chain/runtime"31 "chain/runtime/unsafe"32 "net/url"33 "strings"34)3536var chainDomain = runtime.ChainDomain()3738// Request represents a parsed request.39type Request struct {40 Path string // The path of the request41 Query url.Values // The parsed query parameters42 Realm string // The realm associated with the request43}4445// Parse takes a raw path string and returns a Request object.46// It splits the path into its components and parses any query parameters.47func Parse(rawPath string) *Request {48 // Split the raw path into path and query components49 path, query := splitPathAndQuery(rawPath)5051 // Parse the query string into url.Values52 queryValues, _ := url.ParseQuery(query)5354 return &Request{55 Path: path, // Set the path56 Query: queryValues, // Set the parsed query values57 }58}5960// PathParts returns the segments of the path as a slice of strings.61// It trims leading and trailing slashes and splits the path by slashes.62func (r *Request) PathParts() []string {63 return strings.Split(strings.Trim(r.Path, "/"), "/")64}6566// PathPart returns the specified part of the path.67// If the index is out of bounds, it returns an empty string.68func (r *Request) PathPart(index int) string {69 parts := r.PathParts() // Get the path segments70 if index < 0 || index >= len(parts) {71 return "" // Return empty if index is out of bounds72 }73 return parts[index] // Return the specified path part74}7576// String rebuilds the URL from the path and query values.77// If the Realm is not set, it automatically retrieves the current realm path.78//79// SECURITY (Class-2-shaped, intentionally accepted): unsafe.CurrentRealm()80// inside a non-crossing /p/ method is .Title()-vulnerable — it walks past81// non-crossing frames to the most-recent crossing ancestor, not the82// immediate caller. The lazily-captured r.Realm is therefore NOT a reliable83// identity claim under a contrived call chain.84//85// This is acceptable here because r.Realm is consumed only as a URL string86// for rendering (the line below builds reconstructedPath for display); no87// caller in /examples uses it for authorization. If you add a new consumer88// that gates writes/auth on r.Realm, replace the lazy fill with an explicit89// `req.Realm = cur.PkgPath()` set by the caller under rlm.IsCurrent(), or90// switch to a sibling method that takes a realm parameter — see91// docs/resources/gno-security.md for the threat-class taxonomy.92func (r *Request) String() string {93 // Automatically set the Realm if it is not already defined94 if r.Realm == "" {95 r.Realm = unsafe.CurrentRealm().PkgPath() // Get the current realm path96 }9798 // Rebuild the path using the realm and path parts99 relativePkgPath := strings.TrimPrefix(r.Realm, chainDomain) // Trim the chain domain prefix100 reconstructedPath := relativePkgPath + ":" + strings.Join(r.PathParts(), "/")101102 // Rebuild the query string103 queryString := r.Query.Encode() // Encode the query parameters104 if queryString != "" {105 return reconstructedPath + "?" + queryString // Return the full URL with query106 }107 return reconstructedPath // Return the path without query parameters108}109110func splitPathAndQuery(rawPath string) (string, string) {111 if idx := strings.Index(rawPath, "?"); idx != -1 {112 return rawPath[:idx], rawPath[idx+1:] // Split at the first '?' found113 }114 return rawPath, "" // No query string present115}116Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.