Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

realmpath.gno

4.27 Kb · 115 lines
  1// Package realmpath is a lightweight Render.path parsing and link generation
  2// library with an idiomatic API, closely resembling that of net/url.
  3//
  4// This package provides utilities for parsing request paths and query
  5// parameters, allowing you to extract path segments and manipulate query
  6// 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 parameters
 14//	    path = "hello/world?foo=bar&baz=foobar"
 15//	    req := realmpath.Parse(path)
 16//
 17//	    // Accessing parsed path and query parameters
 18//	    println(req.Path)             // Output: hello/world
 19//	    println(req.PathPart(0))      // Output: hello
 20//	    println(req.PathPart(1))      // Output: world
 21//	    println(req.Query.Get("foo")) // Output: bar
 22//	    println(req.Query.Get("baz")) // Output: foobar
 23//
 24//	    // Rebuilding the URL
 25//	    println(req.String())         // Output: /r/current/realm:hello/world?baz=foobar&foo=bar
 26//	}
 27package realmpath
 28
 29import (
 30	"chain/runtime"
 31	"chain/runtime/unsafe"
 32	"net/url"
 33	"strings"
 34)
 35
 36var chainDomain = runtime.ChainDomain()
 37
 38// Request represents a parsed request.
 39type Request struct {
 40	Path  string     // The path of the request
 41	Query url.Values // The parsed query parameters
 42	Realm string     // The realm associated with the request
 43}
 44
 45// 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 components
 49	path, query := splitPathAndQuery(rawPath)
 50
 51	// Parse the query string into url.Values
 52	queryValues, _ := url.ParseQuery(query)
 53
 54	return &Request{
 55		Path:  path,        // Set the path
 56		Query: queryValues, // Set the parsed query values
 57	}
 58}
 59
 60// 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}
 65
 66// 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 segments
 70	if index < 0 || index >= len(parts) {
 71		return "" // Return empty if index is out of bounds
 72	}
 73	return parts[index] // Return the specified path part
 74}
 75
 76// 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 past
 81// non-crossing frames to the most-recent crossing ancestor, not the
 82// immediate caller. The lazily-captured r.Realm is therefore NOT a reliable
 83// identity claim under a contrived call chain.
 84//
 85// This is acceptable here because r.Realm is consumed only as a URL string
 86// for rendering (the line below builds reconstructedPath for display); no
 87// caller in /examples uses it for authorization. If you add a new consumer
 88// that gates writes/auth on r.Realm, replace the lazy fill with an explicit
 89// `req.Realm = cur.PkgPath()` set by the caller under rlm.IsCurrent(), or
 90// switch to a sibling method that takes a realm parameter — see
 91// 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 defined
 94	if r.Realm == "" {
 95		r.Realm = unsafe.CurrentRealm().PkgPath() // Get the current realm path
 96	}
 97
 98	// Rebuild the path using the realm and path parts
 99	relativePkgPath := strings.TrimPrefix(r.Realm, chainDomain) // Trim the chain domain prefix
100	reconstructedPath := relativePkgPath + ":" + strings.Join(r.PathParts(), "/")
101
102	// Rebuild the query string
103	queryString := r.Query.Encode() // Encode the query parameters
104	if queryString != "" {
105		return reconstructedPath + "?" + queryString // Return the full URL with query
106	}
107	return reconstructedPath // Return the path without query parameters
108}
109
110func splitPathAndQuery(rawPath string) (string, string) {
111	if idx := strings.Index(rawPath, "?"); idx != -1 {
112		return rawPath[:idx], rawPath[idx+1:] // Split at the first '?' found
113	}
114	return rawPath, "" // No query string present
115}