PathrockNetwork Gno Explorer
HomeBlocksTransactionsRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/p/moul/pageable/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
moul / pageable
Files
3 (README)(gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/moul/pageable/v0
gno
0.9

Files (3)

  • README.mdmarkdown
  • gnomod.tomltoml
  • pageable.gnogno
pageable.gnogno
1// Package pageable implements a flexible pagination system that can be used with any data structure2// that implements the Pageable interface. It provides functionality for:3//4//   - Paginating through collections of items5//   - Configurable page sizes6//   - Forward and reverse iteration7//   - URL query parameter parsing8//   - Markdown-based pagination UI9//10// This package is currently used by:11//   - gno.land/p/nt/avl/pager: implementation for AVL trees.12//   - gno.land/p/moul/ulist/pager: implementation for ulist13package pageable1415import (16	"math"17	"net/url"18	"strconv"1920	"gno.land/p/nt/ufmt/v0"21)2223// Pageable defines the minimal interface required for pagination.24// This interface is intentionally lightweight, requiring only two methods.25// While some data structures (like AVL trees) might have separate methods for forward26// and reverse iteration, this interface consolidates both directions into a single27// IterateByOffset method. It's the responsibility of the implementing wrapper to28// handle the direction logic internally based on the offset and count parameters.29type Pageable interface {30	// Size returns the total number of items31	Size() int3233	// IterateByOffset performs iteration starting from offset for count elements.34	// The callback receives an index and a value, returns true to stop iteration.35	//36	// For reverse iteration, the implementation should handle the direction internally37	// by adjusting how it interprets the offset and count parameters. For example:38	// - In forward mode: offset 0, count 5 would return items [0,1,2,3,4]39	// - In reverse mode: offset 0, count -5 would return items [4,3,2,1,0]40	//41	// This approach allows the interface to remain simple while supporting42	// bidirectional iteration through the implementation layer.43	IterateByOffset(offset int, count int, cb func(index interface{}, value interface{}) bool) bool44}4546// Pager provides pagination functionality for any Pageable source47type Pager struct {48	Source          Pageable49	PageQueryParam  string50	SizeQueryParam  string51	DefaultPageSize int52	Reversed        bool53}5455// Page represents a single page of results56type Page struct {57	Items      []Item58	PageNumber int59	PageSize   int60	TotalItems int61	TotalPages int62	HasPrev    bool63	HasNext    bool64	Pager      *Pager65}6667// Item represents a generic item in the page68type Item struct {69	Index interface{}70	Value interface{}71}7273// NewPager creates a new Pager with default values74func NewPager(source Pageable, defaultPageSize int, reversed bool) *Pager {75	return &Pager{76		Source:          source,77		PageQueryParam:  "page",78		SizeQueryParam:  "size",79		DefaultPageSize: defaultPageSize,80		Reversed:        reversed,81	}82}8384// GetPage retrieves a page of results from the AVL tree.85func (p *Pager) GetPage(pageNumber int) *Page {86	return p.GetPageWithSize(pageNumber, p.DefaultPageSize)87}8889// Helper function to create a basic page90func (p *Pager) newBasePage(pageSize int) *Page {91	totalItems := p.Source.Size()92	totalPages := 093	if pageSize > 0 {94		totalPages = int(math.Ceil(float64(totalItems) / float64(pageSize)))95	}9697	return &Page{98		TotalItems: totalItems,99		TotalPages: totalPages,100		PageSize:   pageSize,101		Pager:      p,102	}103}104105func (p *Pager) GetPageWithSize(pageNumber, pageSize int) *Page {106	// Handle invalid page size107	if pageSize <= 0 {108		return p.newBasePage(pageSize)109	}110111	page := p.newBasePage(pageSize)112113	// For empty source, return empty page with no navigation114	if page.TotalItems == 0 {115		page.HasPrev = false116		page.HasNext = false117		return page118	}119120	// page number provided is not available121	if pageNumber < 1 {122		page.HasNext = page.TotalPages > 0123		return page124	}125126	// page number provided is outside the range of total pages127	if pageNumber > page.TotalPages {128		page.PageNumber = pageNumber129		page.HasPrev = page.TotalPages > 0130		return page131	}132133	// Calculate offset and size134	var offset, iterCount int135	if p.Reversed {136		// For reversed order, calculate from the end137		iterCount = min(pageSize, page.TotalItems-((pageNumber-1)*pageSize))138		offset = max(0, page.TotalItems-(pageNumber*pageSize))139	} else {140		offset = (pageNumber - 1) * pageSize141		iterCount = pageSize142	}143144	// Collect items for the current page145	var items []Item146	if p.Reversed {147		// For reversed order, use negative count to indicate reverse iteration148		offset = page.TotalItems - offset - iterCount149		p.Source.IterateByOffset(offset, -iterCount, func(index interface{}, value interface{}) bool {150			items = append(items, Item{Index: index, Value: value})151			return false152		})153	} else {154		p.Source.IterateByOffset(offset, iterCount, func(index interface{}, value interface{}) bool {155			items = append(items, Item{Index: index, Value: value})156			return false157		})158	}159160	page.Items = items161	page.PageNumber = pageNumber162	page.HasPrev = pageNumber > 1163	page.HasNext = pageNumber < page.TotalPages164	return page165}166167func (p *Pager) MustGetPageByPath(rawURL string) *Page {168	page, err := p.GetPageByPath(rawURL)169	if err != nil {170		panic("invalid path")171	}172	return page173}174175// GetPageByPath retrieves a page of results based on the query parameters in the URL path.176func (p *Pager) GetPageByPath(rawURL string) (*Page, error) {177	pageNumber, pageSize, err := p.ParseQuery(rawURL)178	if err != nil {179		return nil, err180	}181	return p.GetPageWithSize(pageNumber, pageSize), nil182}183184// Picker generates the Markdown UI for the page picker using default query string format185func (p *Page) Picker() string {186	return p.PickerWithPath("")187}188189// PickerWithPath generates the Markdown UI for the page picker with optional path190// If path is provided, it preserves existing query parameters (except page)191func (p *Page) PickerWithPath(path string) string {192	pageNumber := p.PageNumber193	pageNumber = max(pageNumber, 1)194195	if p.TotalPages <= 1 {196		return ""197	}198199	// Parse existing query parameters if path is provided200	baseQuery := ""201	if path != "" {202		u, _ := url.Parse(path)203		if u != nil {204			query := u.Query()205			// Remove existing page query parameter206			query.Del(p.Pager.PageQueryParam)207			// Encode remaining query parameters208			baseQuery = query.Encode()209			if baseQuery != "" {210				baseQuery = "&" + baseQuery211			}212		}213	}214215	md := ""216217	if p.HasPrev {218		// Always show the first page link219		md += ufmt.Sprintf("[%d](?%s=%d%s) | ", 1, p.Pager.PageQueryParam, 1, baseQuery)220221		// Before222		if p.PageNumber > 4 {223			md += "… | "224		}225226		if p.PageNumber > 3 {227			md += ufmt.Sprintf("[%d](?%s=%d%s) | ", p.PageNumber-2, p.Pager.PageQueryParam, p.PageNumber-2, baseQuery)228		}229230		if p.PageNumber > 2 {231			md += ufmt.Sprintf("[%d](?%s=%d%s) | ", p.PageNumber-1, p.Pager.PageQueryParam, p.PageNumber-1, baseQuery)232		}233	}234235	if p.PageNumber > 0 && p.PageNumber <= p.TotalPages {236		// Current page237		md += ufmt.Sprintf("**%d**", p.PageNumber)238	} else {239		md += ufmt.Sprintf("_%d_", p.PageNumber)240	}241242	if p.HasNext {243		md += " | "244245		if p.PageNumber < p.TotalPages-1 {246			md += ufmt.Sprintf("[%d](?%s=%d%s) | ", p.PageNumber+1, p.Pager.PageQueryParam, p.PageNumber+1, baseQuery)247		}248249		if p.PageNumber < p.TotalPages-2 {250			md += ufmt.Sprintf("[%d](?%s=%d%s) | ", p.PageNumber+2, p.Pager.PageQueryParam, p.PageNumber+2, baseQuery)251		}252253		if p.PageNumber < p.TotalPages-3 {254			md += "… | "255		}256257		// Always show the last page link258		md += ufmt.Sprintf("[%d](?%s=%d%s)", p.TotalPages, p.Pager.PageQueryParam, p.TotalPages, baseQuery)259	}260261	return md262}263264// Helper to safely parse positive integers with a default value265func parsePositiveIntOrDefault(str string, defaultValue int) int {266	if str == "" {267		return defaultValue268	}269	val, err := strconv.Atoi(str)270	if err != nil || val < 1 {271		return defaultValue272	}273	return val274}275276func (p *Pager) ParseQuery(rawURL string) (int, int, error) {277	u, err := url.Parse(rawURL)278	if err != nil {279		return 1, p.DefaultPageSize, err280	}281282	query := u.Query()283	pageNumber := parsePositiveIntOrDefault(query.Get(p.PageQueryParam), 1)284	pageSize := parsePositiveIntOrDefault(query.Get(p.SizeQueryParam), p.DefaultPageSize)285286	return pageNumber, pageSize, nil287}288289func max(a, b int) int {290	if a > b {291		return a292	}293	return b294}295296func min(a, b int) int {297	if a < b {298		return a299	}300	return b301}302

Functions

not supported for pure packages by the node (vm/qfuncs)

Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.