From ac0d79ed64c7430c69c05a17851370559026aec9 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 07:59:03 +0200 Subject: [PATCH 01/22] feat: add p/avl/pager Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/pagination/gno.mod | 3 + .../gno.land/p/demo/pagination/pagination.gno | 135 ++++++++++++++++++ .../p/demo/pagination/pagination_test.gno | 112 +++++++++++++++ 3 files changed, 250 insertions(+) create mode 100644 examples/gno.land/p/demo/pagination/gno.mod create mode 100644 examples/gno.land/p/demo/pagination/pagination.gno create mode 100644 examples/gno.land/p/demo/pagination/pagination_test.gno diff --git a/examples/gno.land/p/demo/pagination/gno.mod b/examples/gno.land/p/demo/pagination/gno.mod new file mode 100644 index 00000000000..cf3f7587312 --- /dev/null +++ b/examples/gno.land/p/demo/pagination/gno.mod @@ -0,0 +1,3 @@ +module gno.land/p/demo/pagination + +require gno.land/p/demo/avl v0.0.0-latest diff --git a/examples/gno.land/p/demo/pagination/pagination.gno b/examples/gno.land/p/demo/pagination/pagination.gno new file mode 100644 index 00000000000..30851e9eb20 --- /dev/null +++ b/examples/gno.land/p/demo/pagination/pagination.gno @@ -0,0 +1,135 @@ +package pagination + +import ( + "math" + + "gno.land/p/demo/avl" + "gno.land/p/demo/ufmt" +) + +// Paginator is a struct that holds the AVL tree and pagination parameters. +type Paginator struct { + Tree *avl.Tree +} + +// Page represents a single page of results. +type Page struct { + Items []Item + PageNumber int + PageSize int + TotalItems int + TotalPages int + HasPrev bool + HasNext bool +} + +// Item represents a key-value pair in the AVL tree. +type Item struct { + Key string + Value interface{} +} + +// GetPage retrieves a page of results from the AVL tree. +func (p *Paginator) GetPage(pageNumber int, pageSize int) (*Page, error) { + if pageNumber < 1 || pageSize < 1 { + return nil, ufmt.Errorf("invalid page number or page size") + } + + totalItems := p.Tree.Size() + totalPages := int(math.Ceil(float64(totalItems) / float64(pageSize))) + + if pageNumber > totalPages { + return &Page{ + Items: []Item{}, + PageNumber: pageNumber, + PageSize: pageSize, + TotalItems: totalItems, + TotalPages: totalPages, + HasPrev: pageNumber > 1, + HasNext: pageNumber < totalPages, + }, nil + } + + startIndex := (pageNumber - 1) * pageSize + endIndex := startIndex + pageSize + if endIndex > totalItems { + endIndex = totalItems + } + + items := []Item{} + p.Tree.IterateByOffset(startIndex, endIndex-startIndex, func(key string, value interface{}) bool { + items = append(items, Item{Key: key, Value: value}) + return false + }) + + return &Page{ + Items: items, + PageNumber: pageNumber, + PageSize: pageSize, + TotalItems: totalItems, + TotalPages: totalPages, + HasPrev: pageNumber > 1, + HasNext: pageNumber < totalPages, + }, nil +} + +// RenderPageSelector generates the Markdown for the page selector. +func (p *Paginator) RenderPageSelector(page *Page) string { + if page.TotalPages <= 1 { + return "" + } + + md := "" + + // First and Previous links + if page.HasPrev { + md += ufmt.Sprintf("[First](?page=1) | ") + md += ufmt.Sprintf("[Previous](?page=%d) | ", page.PageNumber-1) + } else { + md += "First | Previous | " + } + + // Page number links + startPage := max(1, page.PageNumber-2) + endPage := min(page.TotalPages, page.PageNumber+2) + + if startPage > 1 { + md += "… | " + } + + for i := startPage; i <= endPage; i++ { + if i == page.PageNumber { + md += ufmt.Sprintf("**%d** | ", i) + } else { + md += ufmt.Sprintf("[%d](?page=%d) | ", i, i) + } + } + + if endPage < page.TotalPages { + md += "… | " + } + + // Next and Last links + if page.HasNext { + md += ufmt.Sprintf("[Next](?page=%d) | ", page.PageNumber+1) + md += ufmt.Sprintf("[Last](?page=%d)", page.TotalPages) + } else { + md += "Next | Last" + } + + return md +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/examples/gno.land/p/demo/pagination/pagination_test.gno b/examples/gno.land/p/demo/pagination/pagination_test.gno new file mode 100644 index 00000000000..faef4702f8a --- /dev/null +++ b/examples/gno.land/p/demo/pagination/pagination_test.gno @@ -0,0 +1,112 @@ +package pagination + +import ( + "testing" + + "gno.land/p/demo/avl" + "gno.land/p/demo/uassert" + "gno.land/p/demo/ufmt" + "gno.land/p/demo/urequire" +) + +func TestPaginator_GetPage(t *testing.T) { + // Create a new AVL tree and populate it with some key-value pairs. + tree := avl.NewTree() + tree.Set("a", 1) + tree.Set("b", 2) + tree.Set("c", 3) + tree.Set("d", 4) + tree.Set("e", 5) + + // Create a new paginator. + paginator := Paginator{Tree: tree} + + // Define test cases. + tests := []struct { + pageNumber int + pageSize int + expected []Item + }{ + {1, 2, []Item{{Key: "a", Value: 1}, {Key: "b", Value: 2}}}, + {2, 2, []Item{{Key: "c", Value: 3}, {Key: "d", Value: 4}}}, + {3, 2, []Item{{Key: "e", Value: 5}}}, + {1, 3, []Item{{Key: "a", Value: 1}, {Key: "b", Value: 2}, {Key: "c", Value: 3}}}, + {2, 3, []Item{{Key: "d", Value: 4}, {Key: "e", Value: 5}}}, + {1, 5, []Item{{Key: "a", Value: 1}, {Key: "b", Value: 2}, {Key: "c", Value: 3}, {Key: "d", Value: 4}, {Key: "e", Value: 5}}}, + {2, 5, []Item{}}, + } + + for _, tt := range tests { + page, err := paginator.GetPage(tt.pageNumber, tt.pageSize) + urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) + + uassert.Equal(t, len(tt.expected), len(page.Items), ufmt.Sprintf("GetPage(%d, %d) returned %d items, expected %d", tt.pageNumber, tt.pageSize, len(page.Items), len(tt.expected))) + + for i, item := range page.Items { + uassert.Equal(t, tt.expected[i].Key, item.Key, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item, tt.expected[i])) + uassert.Equal(t, tt.expected[i].Value, item.Value, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item, tt.expected[i])) + } + } +} + +func TestPaginator_RenderPageSelector(t *testing.T) { + // Create a new AVL tree and populate it with some key-value pairs. + tree := avl.NewTree() + tree.Set("a", 1) + tree.Set("b", 2) + tree.Set("c", 3) + tree.Set("d", 4) + tree.Set("e", 5) + + // Create a new paginator. + paginator := Paginator{Tree: tree} + + // Define test cases. + tests := []struct { + pageNumber int + pageSize int + expected string + }{ + {1, 2, "First | Previous | **1** | [2](?page=2) | [3](?page=3) | … | [Next](?page=2) | [Last](?page=3)"}, + {2, 2, "[First](?page=1) | [Previous](?page=1) | [1](?page=1) | **2** | [3](?page=3) | … | [Next](?page=3) | [Last](?page=3)"}, + {3, 2, "[First](?page=1) | [Previous](?page=2) | … | [2](?page=2) | **3** | Next | Last"}, + } + + for _, tt := range tests { + page, err := paginator.GetPage(tt.pageNumber, tt.pageSize) + urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) + + md := paginator.RenderPageSelector(page) + uassert.Equal(t, tt.expected, md, ufmt.Sprintf("RenderPageSelector(%d, %d) returned %s, expected %s", tt.pageNumber, tt.pageSize, md, tt.expected)) + } +} + +func TestPaginator_RenderPageSelector_WithManyPages(t *testing.T) { + // Create a new AVL tree and populate it with many key-value pairs. + tree := avl.NewTree() + for i := 0; i < 100; i++ { + tree.Set(ufmt.Sprintf("key%d", i), i) + } + + // Create a new paginator. + paginator := Paginator{Tree: tree} + + // Define test cases for a large number of pages. + tests := []struct { + pageNumber int + pageSize int + expected string + }{ + {1, 10, "First | Previous | **1** | [2](?page=2) | [3](?page=3) | … | [Next](?page=2) | [Last](?page=10)"}, + {5, 10, "[First](?page=1) | [Previous](?page=4) | … | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) | [7](?page=7) | … | [Next](?page=6) | [Last](?page=10)"}, + {10, 10, "[First](?page=1) | [Previous](?page=9) | … | [8](?page=8) | [9](?page=9) | **10** | Next | Last"}, + } + + for _, tt := range tests { + page, err := paginator.GetPage(tt.pageNumber, tt.pageSize) + urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) + + md := paginator.RenderPageSelector(page) + uassert.Equal(t, tt.expected, md, ufmt.Sprintf("RenderPageSelector(%d, %d) returned %s, expected %s", tt.pageNumber, tt.pageSize, md, tt.expected)) + } +} From a17edb25399b329303d552e756616ac225ff6749 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 08:31:52 +0200 Subject: [PATCH 02/22] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/pager/gno.mod | 8 +++ .../pagination.gno => avl/pager/pager.gno} | 54 +++++++++++-------- .../pager/pager_test.gno} | 38 ++++++------- examples/gno.land/p/demo/pagination/gno.mod | 3 -- 4 files changed, 60 insertions(+), 43 deletions(-) create mode 100644 examples/gno.land/p/demo/avl/pager/gno.mod rename examples/gno.land/p/demo/{pagination/pagination.gno => avl/pager/pager.gno} (62%) rename examples/gno.land/p/demo/{pagination/pagination_test.gno => avl/pager/pager_test.gno} (75%) delete mode 100644 examples/gno.land/p/demo/pagination/gno.mod diff --git a/examples/gno.land/p/demo/avl/pager/gno.mod b/examples/gno.land/p/demo/avl/pager/gno.mod new file mode 100644 index 00000000000..59c961d73f2 --- /dev/null +++ b/examples/gno.land/p/demo/avl/pager/gno.mod @@ -0,0 +1,8 @@ +module gno.land/p/demo/avl/pager + +require ( + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/uassert v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest + gno.land/p/demo/urequire v0.0.0-latest +) diff --git a/examples/gno.land/p/demo/pagination/pagination.gno b/examples/gno.land/p/demo/avl/pager/pager.gno similarity index 62% rename from examples/gno.land/p/demo/pagination/pagination.gno rename to examples/gno.land/p/demo/avl/pager/pager.gno index 30851e9eb20..29033fb46c1 100644 --- a/examples/gno.land/p/demo/pagination/pagination.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -1,4 +1,4 @@ -package pagination +package pager import ( "math" @@ -7,8 +7,8 @@ import ( "gno.land/p/demo/ufmt" ) -// Paginator is a struct that holds the AVL tree and pagination parameters. -type Paginator struct { +// Pager is a struct that holds the AVL tree and pagination parameters. +type Pager struct { Tree *avl.Tree } @@ -30,14 +30,26 @@ type Item struct { } // GetPage retrieves a page of results from the AVL tree. -func (p *Paginator) GetPage(pageNumber int, pageSize int) (*Page, error) { - if pageNumber < 1 || pageSize < 1 { - return nil, ufmt.Errorf("invalid page number or page size") +func (p *Pager) GetPage(pageNumber int, pageSize int) (*Page, error) { + if pageSize < 1 { + return nil, ufmt.Errorf("invalid page size") } totalItems := p.Tree.Size() totalPages := int(math.Ceil(float64(totalItems) / float64(pageSize))) + if pageNumber < 1 { + return &Page{ + Items: []Item{}, + PageNumber: 0, + PageSize: pageSize, + TotalItems: totalItems, + TotalPages: totalPages, + HasPrev: false, + HasNext: pageNumber < totalPages, + }, nil + } + if pageNumber > totalPages { return &Page{ Items: []Item{}, @@ -45,8 +57,8 @@ func (p *Paginator) GetPage(pageNumber int, pageSize int) (*Page, error) { PageSize: pageSize, TotalItems: totalItems, TotalPages: totalPages, - HasPrev: pageNumber > 1, - HasNext: pageNumber < totalPages, + HasPrev: totalPages > 0, + HasNext: false, }, nil } @@ -57,7 +69,7 @@ func (p *Paginator) GetPage(pageNumber int, pageSize int) (*Page, error) { } items := []Item{} - p.Tree.IterateByOffset(startIndex, endIndex-startIndex, func(key string, value interface{}) bool { + p.Tree.ReverseIterateByOffset(startIndex, endIndex-startIndex, func(key string, value interface{}) bool { items = append(items, Item{Key: key, Value: value}) return false }) @@ -73,46 +85,46 @@ func (p *Paginator) GetPage(pageNumber int, pageSize int) (*Page, error) { }, nil } -// RenderPageSelector generates the Markdown for the page selector. -func (p *Paginator) RenderPageSelector(page *Page) string { - if page.TotalPages <= 1 { +// RenderSelector generates the Markdown for the page selector. +func (p *Page) RenderSelector() string { + if p.TotalPages <= 1 { return "" } md := "" // First and Previous links - if page.HasPrev { + if p.HasPrev { md += ufmt.Sprintf("[First](?page=1) | ") - md += ufmt.Sprintf("[Previous](?page=%d) | ", page.PageNumber-1) + md += ufmt.Sprintf("[Previous](?page=%d) | ", p.PageNumber-1) } else { md += "First | Previous | " } // Page number links - startPage := max(1, page.PageNumber-2) - endPage := min(page.TotalPages, page.PageNumber+2) + startPage := max(1, p.PageNumber-2) + endPage := min(p.TotalPages, p.PageNumber+2) if startPage > 1 { md += "… | " } for i := startPage; i <= endPage; i++ { - if i == page.PageNumber { + if i == p.PageNumber { md += ufmt.Sprintf("**%d** | ", i) } else { md += ufmt.Sprintf("[%d](?page=%d) | ", i, i) } } - if endPage < page.TotalPages { + if endPage < p.TotalPages { md += "… | " } // Next and Last links - if page.HasNext { - md += ufmt.Sprintf("[Next](?page=%d) | ", page.PageNumber+1) - md += ufmt.Sprintf("[Last](?page=%d)", page.TotalPages) + if p.HasNext { + md += ufmt.Sprintf("[Next](?page=%d) | ", p.PageNumber+1) + md += ufmt.Sprintf("[Last](?page=%d)", p.TotalPages) } else { md += "Next | Last" } diff --git a/examples/gno.land/p/demo/pagination/pagination_test.gno b/examples/gno.land/p/demo/avl/pager/pager_test.gno similarity index 75% rename from examples/gno.land/p/demo/pagination/pagination_test.gno rename to examples/gno.land/p/demo/avl/pager/pager_test.gno index faef4702f8a..ac8d0add82f 100644 --- a/examples/gno.land/p/demo/pagination/pagination_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -1,4 +1,4 @@ -package pagination +package pager import ( "testing" @@ -9,7 +9,7 @@ import ( "gno.land/p/demo/urequire" ) -func TestPaginator_GetPage(t *testing.T) { +func TestPager_GetPage(t *testing.T) { // Create a new AVL tree and populate it with some key-value pairs. tree := avl.NewTree() tree.Set("a", 1) @@ -18,8 +18,8 @@ func TestPaginator_GetPage(t *testing.T) { tree.Set("d", 4) tree.Set("e", 5) - // Create a new paginator. - paginator := Paginator{Tree: tree} + // Create a new pager. + pager := Pager{Tree: tree} // Define test cases. tests := []struct { @@ -37,19 +37,19 @@ func TestPaginator_GetPage(t *testing.T) { } for _, tt := range tests { - page, err := paginator.GetPage(tt.pageNumber, tt.pageSize) + page, err := pager.GetPage(tt.pageNumber, tt.pageSize) urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) uassert.Equal(t, len(tt.expected), len(page.Items), ufmt.Sprintf("GetPage(%d, %d) returned %d items, expected %d", tt.pageNumber, tt.pageSize, len(page.Items), len(tt.expected))) for i, item := range page.Items { - uassert.Equal(t, tt.expected[i].Key, item.Key, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item, tt.expected[i])) - uassert.Equal(t, tt.expected[i].Value, item.Value, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item, tt.expected[i])) + uassert.Equal(t, tt.expected[i].Key, item.Key, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item.Key, tt.expected[i].Key)) + uassert.Equal(t, tt.expected[i].Value, item.Value, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item.Value, tt.expected[i].Value)) } } } -func TestPaginator_RenderPageSelector(t *testing.T) { +func TestPage_RenderSelector(t *testing.T) { // Create a new AVL tree and populate it with some key-value pairs. tree := avl.NewTree() tree.Set("a", 1) @@ -58,8 +58,8 @@ func TestPaginator_RenderPageSelector(t *testing.T) { tree.Set("d", 4) tree.Set("e", 5) - // Create a new paginator. - paginator := Paginator{Tree: tree} + // Create a new pager. + pager := Pager{Tree: tree} // Define test cases. tests := []struct { @@ -73,23 +73,23 @@ func TestPaginator_RenderPageSelector(t *testing.T) { } for _, tt := range tests { - page, err := paginator.GetPage(tt.pageNumber, tt.pageSize) + page, err := pager.GetPage(tt.pageNumber, tt.pageSize) urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) - md := paginator.RenderPageSelector(page) - uassert.Equal(t, tt.expected, md, ufmt.Sprintf("RenderPageSelector(%d, %d) returned %s, expected %s", tt.pageNumber, tt.pageSize, md, tt.expected)) + md := page.RenderSelector() + uassert.Equal(t, tt.expected, md, ufmt.Sprintf("RenderSelector(%d, %d) returned %s, expected %s", tt.pageNumber, tt.pageSize, md, tt.expected)) } } -func TestPaginator_RenderPageSelector_WithManyPages(t *testing.T) { +func TestPage_RenderSelector_WithManyPages(t *testing.T) { // Create a new AVL tree and populate it with many key-value pairs. tree := avl.NewTree() for i := 0; i < 100; i++ { tree.Set(ufmt.Sprintf("key%d", i), i) } - // Create a new paginator. - paginator := Paginator{Tree: tree} + // Create a new pager. + pager := Pager{Tree: tree} // Define test cases for a large number of pages. tests := []struct { @@ -103,10 +103,10 @@ func TestPaginator_RenderPageSelector_WithManyPages(t *testing.T) { } for _, tt := range tests { - page, err := paginator.GetPage(tt.pageNumber, tt.pageSize) + page, err := pager.GetPage(tt.pageNumber, tt.pageSize) urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) - md := paginator.RenderPageSelector(page) - uassert.Equal(t, tt.expected, md, ufmt.Sprintf("RenderPageSelector(%d, %d) returned %s, expected %s", tt.pageNumber, tt.pageSize, md, tt.expected)) + md := page.RenderSelector() + uassert.Equal(t, tt.expected, md, ufmt.Sprintf("RenderSelector(%d, %d) returned %s, expected %s", tt.pageNumber, tt.pageSize, md, tt.expected)) } } diff --git a/examples/gno.land/p/demo/pagination/gno.mod b/examples/gno.land/p/demo/pagination/gno.mod deleted file mode 100644 index cf3f7587312..00000000000 --- a/examples/gno.land/p/demo/pagination/gno.mod +++ /dev/null @@ -1,3 +0,0 @@ -module gno.land/p/demo/pagination - -require gno.land/p/demo/avl v0.0.0-latest From 1a44f35682c9b7866020b0f42ecdcf90f0c2ed67 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 08:31:56 +0200 Subject: [PATCH 03/22] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- .../gno.land/p/demo/avl/pager/z_filetest.gno | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 examples/gno.land/p/demo/avl/pager/z_filetest.gno diff --git a/examples/gno.land/p/demo/avl/pager/z_filetest.gno b/examples/gno.land/p/demo/avl/pager/z_filetest.gno new file mode 100644 index 00000000000..43437d5f27b --- /dev/null +++ b/examples/gno.land/p/demo/avl/pager/z_filetest.gno @@ -0,0 +1,173 @@ +package main + +import ( + "fmt" + + "gno.land/p/demo/avl" + "gno.land/p/demo/avl/pager" + "gno.land/p/demo/seqid" + "gno.land/p/demo/ufmt" +) + +func main() { + // Create a new AVL tree and populate it with some key-value pairs. + var id seqid.ID + tree := avl.NewTree() + for i := 0; i < 42; i++ { + // tree.Set(fmt.Sprintf("key%d", i), i) + tree.Set(id.Next().String(), i) + } + + // Create a new pager. + pager := pager.Pager{Tree: tree} + + for pn := -1; pn < 8; pn++ { + page, err := pager.GetPage(pn, 7) + if err != nil { + println("Error:", err.Error()) + return + } + + // Print the items on the first page. + println(ufmt.Sprintf("Page %d of %d\n", page.PageNumber, page.TotalPages)) + for _, item := range page.Items { + println(fmt.Sprintf("Key: %s, Value: %v\n", item.Key, item.Value)) + } + + // Render and print the page selector. + pageSelector := page.RenderSelector() + println("Page Selector:") + println(pageSelector) + println() + } +} + +// Output: +// Page 0 of 6 +// +// Page Selector: +// First | Previous | [1](?page=1) | [2](?page=2) | … | [Next](?page=1) | [Last](?page=6) +// +// Page 0 of 6 +// +// Page Selector: +// First | Previous | [1](?page=1) | [2](?page=2) | … | [Next](?page=1) | [Last](?page=6) +// +// Page 1 of 6 +// +// Key: 0000001, Value: 0 +// +// Key: 0000002, Value: 1 +// +// Key: 0000003, Value: 2 +// +// Key: 0000004, Value: 3 +// +// Key: 0000005, Value: 4 +// +// Key: 0000006, Value: 5 +// +// Key: 0000007, Value: 6 +// +// Page Selector: +// First | Previous | **1** | [2](?page=2) | [3](?page=3) | … | [Next](?page=2) | [Last](?page=6) +// +// Page 2 of 6 +// +// Key: 0000008, Value: 7 +// +// Key: 0000009, Value: 8 +// +// Key: 000000a, Value: 9 +// +// Key: 000000b, Value: 10 +// +// Key: 000000c, Value: 11 +// +// Key: 000000d, Value: 12 +// +// Key: 000000e, Value: 13 +// +// Page Selector: +// [First](?page=1) | [Previous](?page=1) | [1](?page=1) | **2** | [3](?page=3) | [4](?page=4) | … | [Next](?page=3) | [Last](?page=6) +// +// Page 3 of 6 +// +// Key: 000000f, Value: 14 +// +// Key: 000000g, Value: 15 +// +// Key: 000000h, Value: 16 +// +// Key: 000000j, Value: 17 +// +// Key: 000000k, Value: 18 +// +// Key: 000000m, Value: 19 +// +// Key: 000000n, Value: 20 +// +// Page Selector: +// [First](?page=1) | [Previous](?page=2) | [1](?page=1) | [2](?page=2) | **3** | [4](?page=4) | [5](?page=5) | … | [Next](?page=4) | [Last](?page=6) +// +// Page 4 of 6 +// +// Key: 000000p, Value: 21 +// +// Key: 000000q, Value: 22 +// +// Key: 000000r, Value: 23 +// +// Key: 000000s, Value: 24 +// +// Key: 000000t, Value: 25 +// +// Key: 000000v, Value: 26 +// +// Key: 000000w, Value: 27 +// +// Page Selector: +// [First](?page=1) | [Previous](?page=3) | … | [2](?page=2) | [3](?page=3) | **4** | [5](?page=5) | [6](?page=6) | [Next](?page=5) | [Last](?page=6) +// +// Page 5 of 6 +// +// Key: 000000x, Value: 28 +// +// Key: 000000y, Value: 29 +// +// Key: 000000z, Value: 30 +// +// Key: 0000010, Value: 31 +// +// Key: 0000011, Value: 32 +// +// Key: 0000012, Value: 33 +// +// Key: 0000013, Value: 34 +// +// Page Selector: +// [First](?page=1) | [Previous](?page=4) | … | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) | [Next](?page=6) | [Last](?page=6) +// +// Page 6 of 6 +// +// Key: 0000014, Value: 35 +// +// Key: 0000015, Value: 36 +// +// Key: 0000016, Value: 37 +// +// Key: 0000017, Value: 38 +// +// Key: 0000018, Value: 39 +// +// Key: 0000019, Value: 40 +// +// Key: 000001a, Value: 41 +// +// Page Selector: +// [First](?page=1) | [Previous](?page=5) | … | [4](?page=4) | [5](?page=5) | **6** | Next | Last +// +// Page 7 of 6 +// +// Page Selector: +// [First](?page=1) | [Previous](?page=6) | … | [5](?page=5) | [6](?page=6) | Next | Last From 291b92fd128b845a984beb085da32a44d8addc30 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 09:02:40 +0200 Subject: [PATCH 04/22] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/pager/pager.gno | 84 ++++++--- .../gno.land/p/demo/avl/pager/pager_test.gno | 68 +++++-- .../gno.land/p/demo/avl/pager/z_filetest.gno | 166 ++++++------------ 3 files changed, 168 insertions(+), 150 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager.gno b/examples/gno.land/p/demo/avl/pager/pager.gno index 29033fb46c1..ad5234254bf 100644 --- a/examples/gno.land/p/demo/avl/pager/pager.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -2,6 +2,8 @@ package pager import ( "math" + "net/url" + "strconv" "gno.land/p/demo/avl" "gno.land/p/demo/ufmt" @@ -9,7 +11,9 @@ import ( // Pager is a struct that holds the AVL tree and pagination parameters. type Pager struct { - Tree *avl.Tree + Tree *avl.Tree + PageQueryParam string + DefaultPageSize int } // Page represents a single page of results. @@ -29,6 +33,15 @@ type Item struct { Value interface{} } +// NewPager creates a new Pager with default values. +func NewPager(tree *avl.Tree) *Pager { + return &Pager{ + Tree: tree, + PageQueryParam: "page", + DefaultPageSize: 10, + } +} + // GetPage retrieves a page of results from the AVL tree. func (p *Pager) GetPage(pageNumber int, pageSize int) (*Page, error) { if pageSize < 1 { @@ -86,52 +99,71 @@ func (p *Pager) GetPage(pageNumber int, pageSize int) (*Page, error) { } // RenderSelector generates the Markdown for the page selector. -func (p *Page) RenderSelector() string { +func (p *Page) RenderSelector(queryParam string) string { if p.TotalPages <= 1 { return "" } md := "" - // First and Previous links - if p.HasPrev { - md += ufmt.Sprintf("[First](?page=1) | ") - md += ufmt.Sprintf("[Previous](?page=%d) | ", p.PageNumber-1) - } else { - md += "First | Previous | " - } - // Page number links - startPage := max(1, p.PageNumber-2) - endPage := min(p.TotalPages, p.PageNumber+2) + if p.PageNumber > 1 { + md += ufmt.Sprintf("[%d](?%s=%d) | ", 1, queryParam, 1) + } - if startPage > 1 { + if p.PageNumber > 2 { md += "… | " } - for i := startPage; i <= endPage; i++ { - if i == p.PageNumber { - md += ufmt.Sprintf("**%d** | ", i) - } else { - md += ufmt.Sprintf("[%d](?page=%d) | ", i, i) - } + if p.HasPrev { + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber-1, queryParam, p.PageNumber-1) + } + + md += ufmt.Sprintf("**%d** | ", p.PageNumber) + + if p.HasNext { + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+1, queryParam, p.PageNumber+1) } - if endPage < p.TotalPages { + if p.PageNumber < p.TotalPages-1 { md += "… | " } - // Next and Last links - if p.HasNext { - md += ufmt.Sprintf("[Next](?page=%d) | ", p.PageNumber+1) - md += ufmt.Sprintf("[Last](?page=%d)", p.TotalPages) - } else { - md += "Next | Last" + if p.PageNumber < p.TotalPages { + md += ufmt.Sprintf("[%d](?%s=%d)", p.TotalPages, queryParam, p.TotalPages) } return md } +// ParseQuery parses the URL to extract the page number and page size. +func (p *Pager) ParseQuery(rawURL string) (int, int, error) { + u, err := url.Parse(rawURL) + if err != nil { + return 1, p.DefaultPageSize, err + } + + query := u.Query() + pageNumber := 1 + pageSize := p.DefaultPageSize + + if pageStr := query.Get(p.PageQueryParam); pageStr != "" { + pageNumber, err = strconv.Atoi(pageStr) + if err != nil || pageNumber < 1 { + pageNumber = 1 + } + } + + if sizeStr := query.Get("size"); sizeStr != "" { + pageSize, err = strconv.Atoi(sizeStr) + if err != nil || pageSize < 1 { + pageSize = p.DefaultPageSize + } + } + + return pageNumber, pageSize, nil +} + func max(a, b int) int { if a > b { return a diff --git a/examples/gno.land/p/demo/avl/pager/pager_test.gno b/examples/gno.land/p/demo/avl/pager/pager_test.gno index ac8d0add82f..502a898dd86 100644 --- a/examples/gno.land/p/demo/avl/pager/pager_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -19,7 +19,7 @@ func TestPager_GetPage(t *testing.T) { tree.Set("e", 5) // Create a new pager. - pager := Pager{Tree: tree} + pager := NewPager(tree) // Define test cases. tests := []struct { @@ -43,8 +43,8 @@ func TestPager_GetPage(t *testing.T) { uassert.Equal(t, len(tt.expected), len(page.Items), ufmt.Sprintf("GetPage(%d, %d) returned %d items, expected %d", tt.pageNumber, tt.pageSize, len(page.Items), len(tt.expected))) for i, item := range page.Items { - uassert.Equal(t, tt.expected[i].Key, item.Key, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item.Key, tt.expected[i].Key)) - uassert.Equal(t, tt.expected[i].Value, item.Value, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item.Value, tt.expected[i].Value)) + uassert.Equal(t, tt.expected[i].Key, item.Key, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item, tt.expected[i])) + uassert.Equal(t, tt.expected[i].Value, item.Value, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item, tt.expected[i])) } } } @@ -59,7 +59,7 @@ func TestPage_RenderSelector(t *testing.T) { tree.Set("e", 5) // Create a new pager. - pager := Pager{Tree: tree} + pager := NewPager(tree) // Define test cases. tests := []struct { @@ -67,21 +67,21 @@ func TestPage_RenderSelector(t *testing.T) { pageSize int expected string }{ - {1, 2, "First | Previous | **1** | [2](?page=2) | [3](?page=3) | … | [Next](?page=2) | [Last](?page=3)"}, - {2, 2, "[First](?page=1) | [Previous](?page=1) | [1](?page=1) | **2** | [3](?page=3) | … | [Next](?page=3) | [Last](?page=3)"}, - {3, 2, "[First](?page=1) | [Previous](?page=2) | … | [2](?page=2) | **3** | Next | Last"}, + {1, 2, "**1** | [2](?page=2) | [3](?page=3) | … | [Next](?page=2) | [Last](?page=3)"}, + {2, 2, "[1](?page=1) | **2** | [3](?page=3) | … | [Next](?page=3) | [Last](?page=3)"}, + {3, 2, "[1](?page=1) | … | [2](?page=2) | **3**"}, } for _, tt := range tests { page, err := pager.GetPage(tt.pageNumber, tt.pageSize) urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) - md := page.RenderSelector() + md := page.RenderSelector(pager.PageQueryParam) uassert.Equal(t, tt.expected, md, ufmt.Sprintf("RenderSelector(%d, %d) returned %s, expected %s", tt.pageNumber, tt.pageSize, md, tt.expected)) } } -func TestPage_RenderSelector_WithManyPages(t *testing.T) { +func TestPager_RenderSelector_WithManyPages(t *testing.T) { // Create a new AVL tree and populate it with many key-value pairs. tree := avl.NewTree() for i := 0; i < 100; i++ { @@ -89,7 +89,7 @@ func TestPage_RenderSelector_WithManyPages(t *testing.T) { } // Create a new pager. - pager := Pager{Tree: tree} + pager := NewPager(tree) // Define test cases for a large number of pages. tests := []struct { @@ -97,16 +97,56 @@ func TestPage_RenderSelector_WithManyPages(t *testing.T) { pageSize int expected string }{ - {1, 10, "First | Previous | **1** | [2](?page=2) | [3](?page=3) | … | [Next](?page=2) | [Last](?page=10)"}, - {5, 10, "[First](?page=1) | [Previous](?page=4) | … | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) | [7](?page=7) | … | [Next](?page=6) | [Last](?page=10)"}, - {10, 10, "[First](?page=1) | [Previous](?page=9) | … | [8](?page=8) | [9](?page=9) | **10** | Next | Last"}, + {1, 10, "**1** | [2](?page=2) | [3](?page=3) | … | [Next](?page=2) | [Last](?page=10)"}, + {5, 10, "[1](?page=1) | … | [4](?page=4) | **5** | [6](?page=6) | [7](?page=7) | … | [Next](?page=6) | [Last](?page=10)"}, + {10, 10, "[1](?page=1) | … | [9](?page=9) | **10**"}, } for _, tt := range tests { page, err := pager.GetPage(tt.pageNumber, tt.pageSize) urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) - md := page.RenderSelector() + md := page.RenderSelector(pager.PageQueryParam) uassert.Equal(t, tt.expected, md, ufmt.Sprintf("RenderSelector(%d, %d) returned %s, expected %s", tt.pageNumber, tt.pageSize, md, tt.expected)) } } + +func TestPager_ParseQuery(t *testing.T) { + // Create a new AVL tree and populate it with some key-value pairs. + tree := avl.NewTree() + tree.Set("a", 1) + tree.Set("b", 2) + tree.Set("c", 3) + tree.Set("d", 4) + tree.Set("e", 5) + + // Create a new pager. + pager := NewPager(tree) + + // Define test cases. + tests := []struct { + rawURL string + expectedPage int + expectedSize int + expectedError bool + }{ + {"https://example.com?size=2&page=1", 1, 2, false}, + {"https://example.com?size=3&page=2", 2, 3, false}, + {"https://example.com?size=5&page=3", 3, 5, false}, + {"https://example.com?page=2", 2, pager.DefaultPageSize, false}, + {"https://example.com?size=3", 1, 3, false}, + {"https://example.com", 1, pager.DefaultPageSize, false}, + {"https://example.com?size=0&page=0", 1, pager.DefaultPageSize, false}, + } + + for _, tt := range tests { + page, size, err := pager.ParseQuery(tt.rawURL) + if tt.expectedError { + uassert.Error(t, err, ufmt.Sprintf("ParseQuery(%s) expected error but got none", tt.rawURL)) + } else { + urequire.NoError(t, err, ufmt.Sprintf("ParseQuery(%s) returned error: %v", tt.rawURL, err)) + uassert.Equal(t, tt.expectedPage, page, ufmt.Sprintf("ParseQuery(%s) returned page %d, expected %d", tt.rawURL, page, tt.expectedPage)) + uassert.Equal(t, tt.expectedSize, size, ufmt.Sprintf("ParseQuery(%s) returned size %d, expected %d", tt.rawURL, size, tt.expectedSize)) + } + } +} diff --git a/examples/gno.land/p/demo/avl/pager/z_filetest.gno b/examples/gno.land/p/demo/avl/pager/z_filetest.gno index 43437d5f27b..14213b942f1 100644 --- a/examples/gno.land/p/demo/avl/pager/z_filetest.gno +++ b/examples/gno.land/p/demo/avl/pager/z_filetest.gno @@ -1,8 +1,6 @@ package main import ( - "fmt" - "gno.land/p/demo/avl" "gno.land/p/demo/avl/pager" "gno.land/p/demo/seqid" @@ -14,12 +12,11 @@ func main() { var id seqid.ID tree := avl.NewTree() for i := 0; i < 42; i++ { - // tree.Set(fmt.Sprintf("key%d", i), i) tree.Set(id.Next().String(), i) } // Create a new pager. - pager := pager.Pager{Tree: tree} + pager := pager.NewPager(tree) for pn := -1; pn < 8; pn++ { page, err := pager.GetPage(pn, 7) @@ -28,14 +25,14 @@ func main() { return } - // Print the items on the first page. - println(ufmt.Sprintf("Page %d of %d\n", page.PageNumber, page.TotalPages)) + // Print the items on the page. + println(ufmt.Sprintf("Page %d of %d", page.PageNumber, page.TotalPages)) for _, item := range page.Items { - println(fmt.Sprintf("Key: %s, Value: %v\n", item.Key, item.Value)) + println(ufmt.Sprintf("Key: %s, Value: %v", item.Key, item.Value)) } // Render and print the page selector. - pageSelector := page.RenderSelector() + pageSelector := page.RenderSelector(pager.PageQueryParam) println("Page Selector:") println(pageSelector) println() @@ -44,130 +41,79 @@ func main() { // Output: // Page 0 of 6 -// // Page Selector: -// First | Previous | [1](?page=1) | [2](?page=2) | … | [Next](?page=1) | [Last](?page=6) +// **0** | [1](?page=1) | … | [6](?page=6) // // Page 0 of 6 -// // Page Selector: -// First | Previous | [1](?page=1) | [2](?page=2) | … | [Next](?page=1) | [Last](?page=6) +// **0** | [1](?page=1) | … | [6](?page=6) // // Page 1 of 6 -// -// Key: 0000001, Value: 0 -// -// Key: 0000002, Value: 1 -// -// Key: 0000003, Value: 2 -// -// Key: 0000004, Value: 3 -// -// Key: 0000005, Value: 4 -// -// Key: 0000006, Value: 5 -// -// Key: 0000007, Value: 6 -// +// Key: 0000001, Value: (unhandled) +// Key: 0000002, Value: (unhandled) +// Key: 0000003, Value: (unhandled) +// Key: 0000004, Value: (unhandled) +// Key: 0000005, Value: (unhandled) +// Key: 0000006, Value: (unhandled) +// Key: 0000007, Value: (unhandled) // Page Selector: -// First | Previous | **1** | [2](?page=2) | [3](?page=3) | … | [Next](?page=2) | [Last](?page=6) +// **1** | [2](?page=2) | … | [6](?page=6) // // Page 2 of 6 -// -// Key: 0000008, Value: 7 -// -// Key: 0000009, Value: 8 -// -// Key: 000000a, Value: 9 -// -// Key: 000000b, Value: 10 -// -// Key: 000000c, Value: 11 -// -// Key: 000000d, Value: 12 -// -// Key: 000000e, Value: 13 -// +// Key: 0000008, Value: (unhandled) +// Key: 0000009, Value: (unhandled) +// Key: 000000a, Value: (unhandled) +// Key: 000000b, Value: (unhandled) +// Key: 000000c, Value: (unhandled) +// Key: 000000d, Value: (unhandled) +// Key: 000000e, Value: (unhandled) // Page Selector: -// [First](?page=1) | [Previous](?page=1) | [1](?page=1) | **2** | [3](?page=3) | [4](?page=4) | … | [Next](?page=3) | [Last](?page=6) +// [1](?page=1) | [1](?page=1) | **2** | [3](?page=3) | … | [6](?page=6) // // Page 3 of 6 -// -// Key: 000000f, Value: 14 -// -// Key: 000000g, Value: 15 -// -// Key: 000000h, Value: 16 -// -// Key: 000000j, Value: 17 -// -// Key: 000000k, Value: 18 -// -// Key: 000000m, Value: 19 -// -// Key: 000000n, Value: 20 -// +// Key: 000000f, Value: (unhandled) +// Key: 000000g, Value: (unhandled) +// Key: 000000h, Value: (unhandled) +// Key: 000000j, Value: (unhandled) +// Key: 000000k, Value: (unhandled) +// Key: 000000m, Value: (unhandled) +// Key: 000000n, Value: (unhandled) // Page Selector: -// [First](?page=1) | [Previous](?page=2) | [1](?page=1) | [2](?page=2) | **3** | [4](?page=4) | [5](?page=5) | … | [Next](?page=4) | [Last](?page=6) +// [1](?page=1) | … | [2](?page=2) | **3** | [4](?page=4) | … | [6](?page=6) // // Page 4 of 6 -// -// Key: 000000p, Value: 21 -// -// Key: 000000q, Value: 22 -// -// Key: 000000r, Value: 23 -// -// Key: 000000s, Value: 24 -// -// Key: 000000t, Value: 25 -// -// Key: 000000v, Value: 26 -// -// Key: 000000w, Value: 27 -// +// Key: 000000p, Value: (unhandled) +// Key: 000000q, Value: (unhandled) +// Key: 000000r, Value: (unhandled) +// Key: 000000s, Value: (unhandled) +// Key: 000000t, Value: (unhandled) +// Key: 000000v, Value: (unhandled) +// Key: 000000w, Value: (unhandled) // Page Selector: -// [First](?page=1) | [Previous](?page=3) | … | [2](?page=2) | [3](?page=3) | **4** | [5](?page=5) | [6](?page=6) | [Next](?page=5) | [Last](?page=6) +// [1](?page=1) | … | [3](?page=3) | **4** | [5](?page=5) | … | [6](?page=6) // // Page 5 of 6 -// -// Key: 000000x, Value: 28 -// -// Key: 000000y, Value: 29 -// -// Key: 000000z, Value: 30 -// -// Key: 0000010, Value: 31 -// -// Key: 0000011, Value: 32 -// -// Key: 0000012, Value: 33 -// -// Key: 0000013, Value: 34 -// +// Key: 000000x, Value: (unhandled) +// Key: 000000y, Value: (unhandled) +// Key: 000000z, Value: (unhandled) +// Key: 0000010, Value: (unhandled) +// Key: 0000011, Value: (unhandled) +// Key: 0000012, Value: (unhandled) +// Key: 0000013, Value: (unhandled) // Page Selector: -// [First](?page=1) | [Previous](?page=4) | … | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) | [Next](?page=6) | [Last](?page=6) +// [1](?page=1) | … | [4](?page=4) | **5** | [6](?page=6) | [6](?page=6) // // Page 6 of 6 -// -// Key: 0000014, Value: 35 -// -// Key: 0000015, Value: 36 -// -// Key: 0000016, Value: 37 -// -// Key: 0000017, Value: 38 -// -// Key: 0000018, Value: 39 -// -// Key: 0000019, Value: 40 -// -// Key: 000001a, Value: 41 -// +// Key: 0000014, Value: (unhandled) +// Key: 0000015, Value: (unhandled) +// Key: 0000016, Value: (unhandled) +// Key: 0000017, Value: (unhandled) +// Key: 0000018, Value: (unhandled) +// Key: 0000019, Value: (unhandled) +// Key: 000001a, Value: (unhandled) // Page Selector: -// [First](?page=1) | [Previous](?page=5) | … | [4](?page=4) | [5](?page=5) | **6** | Next | Last +// [1](?page=1) | … | [5](?page=5) | **6** | // // Page 7 of 6 -// // Page Selector: -// [First](?page=1) | [Previous](?page=6) | … | [5](?page=5) | [6](?page=6) | Next | Last +// [1](?page=1) | … | [6](?page=6) | **7** | From 6038562eb915e89a38f22d3070b0f931901dcd0b Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 10:38:07 +0200 Subject: [PATCH 05/22] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- .../gno.land/p/demo/avl/pager/pager_test.gno | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager_test.gno b/examples/gno.land/p/demo/avl/pager/pager_test.gno index 502a898dd86..827c923347a 100644 --- a/examples/gno.land/p/demo/avl/pager/pager_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -40,11 +40,11 @@ func TestPager_GetPage(t *testing.T) { page, err := pager.GetPage(tt.pageNumber, tt.pageSize) urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) - uassert.Equal(t, len(tt.expected), len(page.Items), ufmt.Sprintf("GetPage(%d, %d) returned %d items, expected %d", tt.pageNumber, tt.pageSize, len(page.Items), len(tt.expected))) + uassert.Equal(t, len(tt.expected), len(page.Items)) for i, item := range page.Items { - uassert.Equal(t, tt.expected[i].Key, item.Key, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item, tt.expected[i])) - uassert.Equal(t, tt.expected[i].Value, item.Value, ufmt.Sprintf("GetPage(%d, %d) returned item %d as %v, expected %v", tt.pageNumber, tt.pageSize, i, item, tt.expected[i])) + uassert.Equal(t, tt.expected[i].Key, item.Key) + uassert.Equal(t, tt.expected[i].Value, item.Value) } } } @@ -67,17 +67,17 @@ func TestPage_RenderSelector(t *testing.T) { pageSize int expected string }{ - {1, 2, "**1** | [2](?page=2) | [3](?page=3) | … | [Next](?page=2) | [Last](?page=3)"}, - {2, 2, "[1](?page=1) | **2** | [3](?page=3) | … | [Next](?page=3) | [Last](?page=3)"}, - {3, 2, "[1](?page=1) | … | [2](?page=2) | **3**"}, + {1, 2, "**1** | [2](?page=2) | [3](?page=3)"}, + {2, 2, "[1](?page=1) | **2** | [3](?page=3)"}, + {3, 2, "[1](?page=1) | [2](?page=2) | **3**"}, } for _, tt := range tests { page, err := pager.GetPage(tt.pageNumber, tt.pageSize) - urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) + urequire.NoError(t, err) md := page.RenderSelector(pager.PageQueryParam) - uassert.Equal(t, tt.expected, md, ufmt.Sprintf("RenderSelector(%d, %d) returned %s, expected %s", tt.pageNumber, tt.pageSize, md, tt.expected)) + uassert.Equal(t, tt.expected, md) } } @@ -97,9 +97,16 @@ func TestPager_RenderSelector_WithManyPages(t *testing.T) { pageSize int expected string }{ - {1, 10, "**1** | [2](?page=2) | [3](?page=3) | … | [Next](?page=2) | [Last](?page=10)"}, - {5, 10, "[1](?page=1) | … | [4](?page=4) | **5** | [6](?page=6) | [7](?page=7) | … | [Next](?page=6) | [Last](?page=10)"}, - {10, 10, "[1](?page=1) | … | [9](?page=9) | **10**"}, + {1, 10, "**1** | [2](?page=2) | [3](?page=3) | … | [10](?page=10)"}, + {2, 10, "[1](?page=1) | **2** | [3](?page=3) | [4](?page=4) | … | [10](?page=10)"}, + {3, 10, "[1](?page=1) | [2](?page=2) | **3** | [4](?page=4) | [5](?page=5) | … | [10](?page=10)"}, + {4, 10, "[1](?page=1) | [2](?page=2) | [3](?page=3) | **4** | [5](?page=5) | [6](?page=6) | … | [10](?page=10)"}, + {5, 10, "[1](?page=1) | [2](?page=2) | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) | [7](?page=7) | … | [10](?page=10)"}, + {6, 10, "[1](?page=1) | … | [4](?page=4) | [5](?page=5) | **6** | [7](?page=7) | [8](?page=8) | [9](?page=9) | [10](?page=10)"}, + {7, 10, "[1](?page=1) | … | [5](?page=5) | [6](?page=6) | **7** | [8](?page=8) | [9](?page=9) | [10](?page=10)"}, + {8, 10, "[1](?page=1) | … | [6](?page=6) | [7](?page=7) | **8** | [9](?page=9) | [10](?page=10)"}, + {9, 10, "[1](?page=1) | … | [7](?page=7) | [8](?page=8) | **9** | [10](?page=10)"}, + {10, 10, "[1](?page=1) | … | [8](?page=8) | [9](?page=9) | **10**"}, } for _, tt := range tests { @@ -107,7 +114,7 @@ func TestPager_RenderSelector_WithManyPages(t *testing.T) { urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) md := page.RenderSelector(pager.PageQueryParam) - uassert.Equal(t, tt.expected, md, ufmt.Sprintf("RenderSelector(%d, %d) returned %s, expected %s", tt.pageNumber, tt.pageSize, md, tt.expected)) + uassert.Equal(t, tt.expected, md) } } From 23db6aafbaaa3305aaf57ef94c5f80ab7c05225d Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 10:55:05 +0200 Subject: [PATCH 06/22] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/pager/pager.gno | 47 +++++++++++++------ .../gno.land/p/demo/avl/pager/pager_test.gno | 8 ++-- .../gno.land/p/demo/avl/pager/z_filetest.gno | 18 +++---- 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager.gno b/examples/gno.land/p/demo/avl/pager/pager.gno index ad5234254bf..8a752fde5ef 100644 --- a/examples/gno.land/p/demo/avl/pager/pager.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -106,30 +106,47 @@ func (p *Page) RenderSelector(queryParam string) string { md := "" - // Page number links - if p.PageNumber > 1 { + if p.HasPrev { + // Always show the first page link md += ufmt.Sprintf("[%d](?%s=%d) | ", 1, queryParam, 1) - } - if p.PageNumber > 2 { - md += "… | " - } + // Before + if p.PageNumber == 5 { + md += ufmt.Sprintf("[%d](?%s=%d) | ", 2, queryParam, 2) + } else if p.PageNumber > 4 { + md += "… | " + } - if p.HasPrev { - md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber-1, queryParam, p.PageNumber-1) + if p.PageNumber > 3 { + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber-2, queryParam, p.PageNumber-2) + } + + if p.PageNumber > 2 { + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber-1, queryParam, p.PageNumber-1) + } } - md += ufmt.Sprintf("**%d** | ", p.PageNumber) + // Current page + md += ufmt.Sprintf("**%d**", p.PageNumber) if p.HasNext { - md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+1, queryParam, p.PageNumber+1) - } + md += " | " - if p.PageNumber < p.TotalPages-1 { - md += "… | " - } + if p.PageNumber < p.TotalPages-1 { + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+1, queryParam, p.PageNumber+1) + } + + if p.PageNumber < p.TotalPages-2 { + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+2, queryParam, p.PageNumber+2) + } + + if p.PageNumber == p.TotalPages-4 { + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+3, queryParam, p.PageNumber+3) + } else if p.PageNumber < p.TotalPages-3 { + md += "… | " + } - if p.PageNumber < p.TotalPages { + // Always show the last page link md += ufmt.Sprintf("[%d](?%s=%d)", p.TotalPages, queryParam, p.TotalPages) } diff --git a/examples/gno.land/p/demo/avl/pager/pager_test.gno b/examples/gno.land/p/demo/avl/pager/pager_test.gno index 827c923347a..3d72989d429 100644 --- a/examples/gno.land/p/demo/avl/pager/pager_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -102,10 +102,10 @@ func TestPager_RenderSelector_WithManyPages(t *testing.T) { {3, 10, "[1](?page=1) | [2](?page=2) | **3** | [4](?page=4) | [5](?page=5) | … | [10](?page=10)"}, {4, 10, "[1](?page=1) | [2](?page=2) | [3](?page=3) | **4** | [5](?page=5) | [6](?page=6) | … | [10](?page=10)"}, {5, 10, "[1](?page=1) | [2](?page=2) | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) | [7](?page=7) | … | [10](?page=10)"}, - {6, 10, "[1](?page=1) | … | [4](?page=4) | [5](?page=5) | **6** | [7](?page=7) | [8](?page=8) | [9](?page=9) | [10](?page=10)"}, - {7, 10, "[1](?page=1) | … | [5](?page=5) | [6](?page=6) | **7** | [8](?page=8) | [9](?page=9) | [10](?page=10)"}, - {8, 10, "[1](?page=1) | … | [6](?page=6) | [7](?page=7) | **8** | [9](?page=9) | [10](?page=10)"}, - {9, 10, "[1](?page=1) | … | [7](?page=7) | [8](?page=8) | **9** | [10](?page=10)"}, + {6, 10, "[1](?page=1) | … | [4](?page=4) | [5](?page=5) | **6** | [7](?page=7) | [8](?page=8) | [9](?page=9) | [10](?page=10)"}, + {7, 10, "[1](?page=1) | … | [5](?page=5) | [6](?page=6) | **7** | [8](?page=8) | [9](?page=9) | [10](?page=10)"}, + {8, 10, "[1](?page=1) | … | [6](?page=6) | [7](?page=7) | **8** | [9](?page=9) | [10](?page=10)"}, + {9, 10, "[1](?page=1) | … | [7](?page=7) | [8](?page=8) | **9** | [10](?page=10)"}, {10, 10, "[1](?page=1) | … | [8](?page=8) | [9](?page=9) | **10**"}, } diff --git a/examples/gno.land/p/demo/avl/pager/z_filetest.gno b/examples/gno.land/p/demo/avl/pager/z_filetest.gno index 14213b942f1..b8a8609e70a 100644 --- a/examples/gno.land/p/demo/avl/pager/z_filetest.gno +++ b/examples/gno.land/p/demo/avl/pager/z_filetest.gno @@ -42,11 +42,11 @@ func main() { // Output: // Page 0 of 6 // Page Selector: -// **0** | [1](?page=1) | … | [6](?page=6) +// **0** | [1](?page=1) | [2](?page=2) | … | [6](?page=6) // // Page 0 of 6 // Page Selector: -// **0** | [1](?page=1) | … | [6](?page=6) +// **0** | [1](?page=1) | [2](?page=2) | … | [6](?page=6) // // Page 1 of 6 // Key: 0000001, Value: (unhandled) @@ -57,7 +57,7 @@ func main() { // Key: 0000006, Value: (unhandled) // Key: 0000007, Value: (unhandled) // Page Selector: -// **1** | [2](?page=2) | … | [6](?page=6) +// **1** | [2](?page=2) | [3](?page=3) | … | [6](?page=6) // // Page 2 of 6 // Key: 0000008, Value: (unhandled) @@ -68,7 +68,7 @@ func main() { // Key: 000000d, Value: (unhandled) // Key: 000000e, Value: (unhandled) // Page Selector: -// [1](?page=1) | [1](?page=1) | **2** | [3](?page=3) | … | [6](?page=6) +// [1](?page=1) | **2** | [3](?page=3) | [4](?page=4) | [5](?page=5) | [6](?page=6) // // Page 3 of 6 // Key: 000000f, Value: (unhandled) @@ -79,7 +79,7 @@ func main() { // Key: 000000m, Value: (unhandled) // Key: 000000n, Value: (unhandled) // Page Selector: -// [1](?page=1) | … | [2](?page=2) | **3** | [4](?page=4) | … | [6](?page=6) +// [1](?page=1) | [2](?page=2) | **3** | [4](?page=4) | [5](?page=5) | [6](?page=6) // // Page 4 of 6 // Key: 000000p, Value: (unhandled) @@ -90,7 +90,7 @@ func main() { // Key: 000000v, Value: (unhandled) // Key: 000000w, Value: (unhandled) // Page Selector: -// [1](?page=1) | … | [3](?page=3) | **4** | [5](?page=5) | … | [6](?page=6) +// [1](?page=1) | [2](?page=2) | [3](?page=3) | **4** | [5](?page=5) | [6](?page=6) // // Page 5 of 6 // Key: 000000x, Value: (unhandled) @@ -101,7 +101,7 @@ func main() { // Key: 0000012, Value: (unhandled) // Key: 0000013, Value: (unhandled) // Page Selector: -// [1](?page=1) | … | [4](?page=4) | **5** | [6](?page=6) | [6](?page=6) +// [1](?page=1) | [2](?page=2) | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) // // Page 6 of 6 // Key: 0000014, Value: (unhandled) @@ -112,8 +112,8 @@ func main() { // Key: 0000019, Value: (unhandled) // Key: 000001a, Value: (unhandled) // Page Selector: -// [1](?page=1) | … | [5](?page=5) | **6** | +// [1](?page=1) | … | [4](?page=4) | [5](?page=5) | **6** // // Page 7 of 6 // Page Selector: -// [1](?page=1) | … | [6](?page=6) | **7** | +// [1](?page=1) | … | [5](?page=5) | [6](?page=6) | **7** From fa7286b150f01bd978b800ca83fdb27c09adbd29 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 11:47:17 +0200 Subject: [PATCH 07/22] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- .../gno.land/p/demo/avl/pager/pager_test.gno | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager_test.gno b/examples/gno.land/p/demo/avl/pager/pager_test.gno index 3d72989d429..b6d3086fa99 100644 --- a/examples/gno.land/p/demo/avl/pager/pager_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -49,6 +49,38 @@ func TestPager_GetPage(t *testing.T) { } } +func TestPager_GetPageByPath(t *testing.T) { + // Create a new AVL tree and populate it with some key-value pairs. + tree := avl.NewTree() + for i := 0; i < 50; i++ { + tree.Set(ufmt.Sprintf("key%d", i), i) + } + + // Create a new pager. + pager := NewPager(tree) + + // Define test cases. + tests := []struct { + rawURL string + expectedPage int + expectedSize int + }{ + {"https://example.com?size=10&page=1", 1, 10}, + {"https://example.com?size=10&page=2", 2, 10}, + {"https://example.com?page=3", 3, pager.DefaultPageSize}, + {"https://example.com?size=20", 1, 20}, + {"https://example.com", 1, pager.DefaultPageSize}, + } + + for _, tt := range tests { + page, err := pager.GetPageByPath(tt.rawURL) + urequire.NoError(t, err, ufmt.Sprintf("GetPageByPath(%s) returned error: %v", tt.rawURL, err)) + + uassert.Equal(t, tt.expectedPage, page.PageNumber) + uassert.Equal(t, tt.expectedSize, page.PageSize) + } +} + func TestPage_RenderSelector(t *testing.T) { // Create a new AVL tree and populate it with some key-value pairs. tree := avl.NewTree() @@ -76,7 +108,7 @@ func TestPage_RenderSelector(t *testing.T) { page, err := pager.GetPage(tt.pageNumber, tt.pageSize) urequire.NoError(t, err) - md := page.RenderSelector(pager.PageQueryParam) + md := page.RenderSelector() uassert.Equal(t, tt.expected, md) } } @@ -106,14 +138,14 @@ func TestPager_RenderSelector_WithManyPages(t *testing.T) { {7, 10, "[1](?page=1) | … | [5](?page=5) | [6](?page=6) | **7** | [8](?page=8) | [9](?page=9) | [10](?page=10)"}, {8, 10, "[1](?page=1) | … | [6](?page=6) | [7](?page=7) | **8** | [9](?page=9) | [10](?page=10)"}, {9, 10, "[1](?page=1) | … | [7](?page=7) | [8](?page=8) | **9** | [10](?page=10)"}, - {10, 10, "[1](?page=1) | … | [8](?page=8) | [9](?page=9) | **10**"}, + {10, 10, "[1](?page=1) | … | [9](?page=9) | **10**"}, } for _, tt := range tests { page, err := pager.GetPage(tt.pageNumber, tt.pageSize) urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) - md := page.RenderSelector(pager.PageQueryParam) + md := page.RenderSelector() uassert.Equal(t, tt.expected, md) } } From c0d435dac622c95e27ba5c29a4338f6cba7ede11 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 13:13:37 +0200 Subject: [PATCH 08/22] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/pager/pager.gno | 31 +++++++++++++------ .../gno.land/p/demo/avl/pager/pager_test.gno | 26 ++++++++-------- .../gno.land/p/demo/avl/pager/z_filetest.gno | 2 +- 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager.gno b/examples/gno.land/p/demo/avl/pager/pager.gno index 8a752fde5ef..d6be969d233 100644 --- a/examples/gno.land/p/demo/avl/pager/pager.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -25,6 +25,7 @@ type Page struct { TotalPages int HasPrev bool HasNext bool + Pager *Pager // Reference to the parent Pager } // Item represents a key-value pair in the AVL tree. @@ -60,6 +61,7 @@ func (p *Pager) GetPage(pageNumber int, pageSize int) (*Page, error) { TotalPages: totalPages, HasPrev: false, HasNext: pageNumber < totalPages, + Pager: p, }, nil } @@ -72,6 +74,7 @@ func (p *Pager) GetPage(pageNumber int, pageSize int) (*Page, error) { TotalPages: totalPages, HasPrev: totalPages > 0, HasNext: false, + Pager: p, }, nil } @@ -95,11 +98,21 @@ func (p *Pager) GetPage(pageNumber int, pageSize int) (*Page, error) { TotalPages: totalPages, HasPrev: pageNumber > 1, HasNext: pageNumber < totalPages, + Pager: p, }, nil } +// GetPageByPath retrieves a page of results based on the query parameters in the URL path. +func (p *Pager) GetPageByPath(rawURL string) (*Page, error) { + pageNumber, pageSize, err := p.ParseQuery(rawURL) + if err != nil { + return nil, err + } + return p.GetPage(pageNumber, pageSize) +} + // RenderSelector generates the Markdown for the page selector. -func (p *Page) RenderSelector(queryParam string) string { +func (p *Page) RenderSelector() string { if p.TotalPages <= 1 { return "" } @@ -108,21 +121,21 @@ func (p *Page) RenderSelector(queryParam string) string { if p.HasPrev { // Always show the first page link - md += ufmt.Sprintf("[%d](?%s=%d) | ", 1, queryParam, 1) + md += ufmt.Sprintf("[%d](?%s=%d) | ", 1, p.Pager.PageQueryParam, 1) // Before if p.PageNumber == 5 { - md += ufmt.Sprintf("[%d](?%s=%d) | ", 2, queryParam, 2) + md += ufmt.Sprintf("[%d](?%s=%d) | ", 2, p.Pager.PageQueryParam, 2) } else if p.PageNumber > 4 { md += "… | " } if p.PageNumber > 3 { - md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber-2, queryParam, p.PageNumber-2) + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber-2, p.Pager.PageQueryParam, p.PageNumber-2) } if p.PageNumber > 2 { - md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber-1, queryParam, p.PageNumber-1) + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber-1, p.Pager.PageQueryParam, p.PageNumber-1) } } @@ -133,21 +146,21 @@ func (p *Page) RenderSelector(queryParam string) string { md += " | " if p.PageNumber < p.TotalPages-1 { - md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+1, queryParam, p.PageNumber+1) + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+1, p.Pager.PageQueryParam, p.PageNumber+1) } if p.PageNumber < p.TotalPages-2 { - md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+2, queryParam, p.PageNumber+2) + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+2, p.Pager.PageQueryParam, p.PageNumber+2) } if p.PageNumber == p.TotalPages-4 { - md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+3, queryParam, p.PageNumber+3) + md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+3, p.Pager.PageQueryParam, p.PageNumber+3) } else if p.PageNumber < p.TotalPages-3 { md += "… | " } // Always show the last page link - md += ufmt.Sprintf("[%d](?%s=%d)", p.TotalPages, queryParam, p.TotalPages) + md += ufmt.Sprintf("[%d](?%s=%d)", p.TotalPages, p.Pager.PageQueryParam, p.TotalPages) } return md diff --git a/examples/gno.land/p/demo/avl/pager/pager_test.gno b/examples/gno.land/p/demo/avl/pager/pager_test.gno index b6d3086fa99..1d392585d1f 100644 --- a/examples/gno.land/p/demo/avl/pager/pager_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -65,11 +65,11 @@ func TestPager_GetPageByPath(t *testing.T) { expectedPage int expectedSize int }{ - {"https://example.com?size=10&page=1", 1, 10}, - {"https://example.com?size=10&page=2", 2, 10}, - {"https://example.com?page=3", 3, pager.DefaultPageSize}, - {"https://example.com?size=20", 1, 20}, - {"https://example.com", 1, pager.DefaultPageSize}, + {"/r/foo:bar/baz?size=10&page=1", 1, 10}, + {"/r/foo:bar/baz?size=10&page=2", 2, 10}, + {"/r/foo:bar/baz?page=3", 3, pager.DefaultPageSize}, + {"/r/foo:bar/baz?size=20", 1, 20}, + {"/r/foo:bar/baz", 1, pager.DefaultPageSize}, } for _, tt := range tests { @@ -138,7 +138,7 @@ func TestPager_RenderSelector_WithManyPages(t *testing.T) { {7, 10, "[1](?page=1) | … | [5](?page=5) | [6](?page=6) | **7** | [8](?page=8) | [9](?page=9) | [10](?page=10)"}, {8, 10, "[1](?page=1) | … | [6](?page=6) | [7](?page=7) | **8** | [9](?page=9) | [10](?page=10)"}, {9, 10, "[1](?page=1) | … | [7](?page=7) | [8](?page=8) | **9** | [10](?page=10)"}, - {10, 10, "[1](?page=1) | … | [9](?page=9) | **10**"}, + {10, 10, "[1](?page=1) | … | [8](?page=8) | [9](?page=9) | **10**"}, } for _, tt := range tests { @@ -169,13 +169,13 @@ func TestPager_ParseQuery(t *testing.T) { expectedSize int expectedError bool }{ - {"https://example.com?size=2&page=1", 1, 2, false}, - {"https://example.com?size=3&page=2", 2, 3, false}, - {"https://example.com?size=5&page=3", 3, 5, false}, - {"https://example.com?page=2", 2, pager.DefaultPageSize, false}, - {"https://example.com?size=3", 1, 3, false}, - {"https://example.com", 1, pager.DefaultPageSize, false}, - {"https://example.com?size=0&page=0", 1, pager.DefaultPageSize, false}, + {"/r/foo:bar/baz?size=2&page=1", 1, 2, false}, + {"/r/foo:bar/baz?size=3&page=2", 2, 3, false}, + {"/r/foo:bar/baz?size=5&page=3", 3, 5, false}, + {"/r/foo:bar/baz?page=2", 2, pager.DefaultPageSize, false}, + {"/r/foo:bar/baz?size=3", 1, 3, false}, + {"/r/foo:bar/baz", 1, pager.DefaultPageSize, false}, + {"/r/foo:bar/baz?size=0&page=0", 1, pager.DefaultPageSize, false}, } for _, tt := range tests { diff --git a/examples/gno.land/p/demo/avl/pager/z_filetest.gno b/examples/gno.land/p/demo/avl/pager/z_filetest.gno index b8a8609e70a..dafe44a9c15 100644 --- a/examples/gno.land/p/demo/avl/pager/z_filetest.gno +++ b/examples/gno.land/p/demo/avl/pager/z_filetest.gno @@ -32,7 +32,7 @@ func main() { } // Render and print the page selector. - pageSelector := page.RenderSelector(pager.PageQueryParam) + pageSelector := page.RenderSelector() println("Page Selector:") println(pageSelector) println() From 1cff9f1916d39a7428295390ff0038fa65896a17 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 13:33:30 +0200 Subject: [PATCH 09/22] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/r/demo/users/users.gno | 34 +++++++++++++++---- .../gno.land/r/demo/users/z_5_filetest.gno | 6 +++- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/examples/gno.land/r/demo/users/users.gno b/examples/gno.land/r/demo/users/users.gno index 9b8e93b579b..ed98fc3f25f 100644 --- a/examples/gno.land/r/demo/users/users.gno +++ b/examples/gno.land/r/demo/users/users.gno @@ -7,6 +7,7 @@ import ( "strings" "gno.land/p/demo/avl" + "gno.land/p/demo/avl/pager" "gno.land/p/demo/users" ) @@ -294,9 +295,10 @@ var reName = regexp.MustCompile(`^[a-z]+[_a-z0-9]{5,16}$`) //---------------------------------------- // Render main page -func Render(path string) string { +func Render(fullPath string) string { + path, qs := splitPathAndQuery(fullPath) if path == "" { - return renderHome() + return renderHome(fullPath) } else if len(path) >= 38 { // 39? 40? if path[:2] != "g1" { return "invalid address " + path @@ -316,12 +318,30 @@ func Render(path string) string { } } -func renderHome() string { +func renderHome(path string) string { doc := "" - name2User.Iterate("", "", func(key string, value interface{}) bool { - user := value.(*users.User) + + p := pager.NewPager(&name2User) + page, err := p.GetPageByPath(path) + if err != nil { + panic("pager error: " + err.Error()) + } + + for _, item := range page.Items { + user := item.Value.(*users.User) doc += " * [" + user.Name + "](/r/demo/users:" + user.Name + ")\n" - return false - }) + } + + doc += page.RenderSelector() return doc } + +func splitPathAndQuery(fullPath string) (string, string) { + parts := strings.SplitN(fullPath, "?", 2) + path := parts[0] + queryString := "" + if len(parts) > 1 { + queryString = "?" + parts[1] + } + return path, queryString +} diff --git a/examples/gno.land/r/demo/users/z_5_filetest.gno b/examples/gno.land/r/demo/users/z_5_filetest.gno index 4ab68ec0e0b..ba360a11267 100644 --- a/examples/gno.land/r/demo/users/z_5_filetest.gno +++ b/examples/gno.land/r/demo/users/z_5_filetest.gno @@ -28,6 +28,8 @@ func main() { users.Register(caller, "satoshi", "my other profile") println(users.Render("")) println("========================================") + println(users.Render("?page=2")) + println("========================================") println(users.Render("gnouser")) println("========================================") println(users.Render("satoshi")) @@ -46,8 +48,10 @@ func main() { // * [nt](/r/demo/users:nt) // * [satoshi](/r/demo/users:satoshi) // * [sys](/r/demo/users:sys) +// **1** | [2](?page=2) +// ======================================== // * [x](/r/demo/users:x) -// +// [1](?page=1) | **2** // ======================================== // ## user gnouser // From b2294ac3bac52e62b634542e159f3384c4f5eea8 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 13:47:46 +0200 Subject: [PATCH 10/22] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/r/demo/users/gno.mod | 1 + examples/gno.land/r/demo/users/users.gno | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/gno.land/r/demo/users/gno.mod b/examples/gno.land/r/demo/users/gno.mod index a2ee2ea86ba..c47eaf3bd28 100644 --- a/examples/gno.land/r/demo/users/gno.mod +++ b/examples/gno.land/r/demo/users/gno.mod @@ -2,5 +2,6 @@ module gno.land/r/demo/users require ( gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/avl/pager v0.0.0-latest gno.land/p/demo/users v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/users/users.gno b/examples/gno.land/r/demo/users/users.gno index ed98fc3f25f..ce5fbdd7742 100644 --- a/examples/gno.land/r/demo/users/users.gno +++ b/examples/gno.land/r/demo/users/users.gno @@ -296,7 +296,7 @@ var reName = regexp.MustCompile(`^[a-z]+[_a-z0-9]{5,16}$`) // Render main page func Render(fullPath string) string { - path, qs := splitPathAndQuery(fullPath) + path, _ := splitPathAndQuery(fullPath) if path == "" { return renderHome(fullPath) } else if len(path) >= 38 { // 39? 40? From 2a1093ed5d2c97719fd34c9ff9e450ec80737cc6 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 13:57:10 +0200 Subject: [PATCH 11/22] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/pager/pager.gno | 22 +++++++++++-------- .../gno.land/p/demo/avl/pager/pager_test.gno | 6 ++--- examples/gno.land/r/demo/users/users.gno | 5 +---- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager.gno b/examples/gno.land/p/demo/avl/pager/pager.gno index d6be969d233..4e6dde41a71 100644 --- a/examples/gno.land/p/demo/avl/pager/pager.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -44,11 +44,7 @@ func NewPager(tree *avl.Tree) *Pager { } // GetPage retrieves a page of results from the AVL tree. -func (p *Pager) GetPage(pageNumber int, pageSize int) (*Page, error) { - if pageSize < 1 { - return nil, ufmt.Errorf("invalid page size") - } - +func (p *Pager) GetPage(pageNumber int, pageSize int) *Page { totalItems := p.Tree.Size() totalPages := int(math.Ceil(float64(totalItems) / float64(pageSize))) @@ -62,7 +58,7 @@ func (p *Pager) GetPage(pageNumber int, pageSize int) (*Page, error) { HasPrev: false, HasNext: pageNumber < totalPages, Pager: p, - }, nil + } } if pageNumber > totalPages { @@ -75,7 +71,7 @@ func (p *Pager) GetPage(pageNumber int, pageSize int) (*Page, error) { HasPrev: totalPages > 0, HasNext: false, Pager: p, - }, nil + } } startIndex := (pageNumber - 1) * pageSize @@ -99,7 +95,15 @@ func (p *Pager) GetPage(pageNumber int, pageSize int) (*Page, error) { HasPrev: pageNumber > 1, HasNext: pageNumber < totalPages, Pager: p, - }, nil + } +} + +func (p *Pager) MustGetPageByPath(rawURL string) *Page { + page, err := p.GetPageByPath(rawURL) + if err != nil { + panic("invalid path") + } + return page } // GetPageByPath retrieves a page of results based on the query parameters in the URL path. @@ -108,7 +112,7 @@ func (p *Pager) GetPageByPath(rawURL string) (*Page, error) { if err != nil { return nil, err } - return p.GetPage(pageNumber, pageSize) + return p.GetPage(pageNumber, pageSize), nil } // RenderSelector generates the Markdown for the page selector. diff --git a/examples/gno.land/p/demo/avl/pager/pager_test.gno b/examples/gno.land/p/demo/avl/pager/pager_test.gno index 1d392585d1f..74ce9b317d4 100644 --- a/examples/gno.land/p/demo/avl/pager/pager_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -37,8 +37,7 @@ func TestPager_GetPage(t *testing.T) { } for _, tt := range tests { - page, err := pager.GetPage(tt.pageNumber, tt.pageSize) - urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) + page := pager.GetPage(tt.pageNumber, tt.pageSize) uassert.Equal(t, len(tt.expected), len(page.Items)) @@ -142,8 +141,7 @@ func TestPager_RenderSelector_WithManyPages(t *testing.T) { } for _, tt := range tests { - page, err := pager.GetPage(tt.pageNumber, tt.pageSize) - urequire.NoError(t, err, ufmt.Sprintf("GetPage(%d, %d) returned error: %v", tt.pageNumber, tt.pageSize, err)) + page := pager.GetPage(tt.pageNumber, tt.pageSize) md := page.RenderSelector() uassert.Equal(t, tt.expected, md) diff --git a/examples/gno.land/r/demo/users/users.gno b/examples/gno.land/r/demo/users/users.gno index ce5fbdd7742..e2cf27d4e8e 100644 --- a/examples/gno.land/r/demo/users/users.gno +++ b/examples/gno.land/r/demo/users/users.gno @@ -322,10 +322,7 @@ func renderHome(path string) string { doc := "" p := pager.NewPager(&name2User) - page, err := p.GetPageByPath(path) - if err != nil { - panic("pager error: " + err.Error()) - } + page := p.MustGetPageByPath(path) for _, item := range page.Items { user := item.Value.(*users.User) From 903fb48b451697684bfc118eacaa7e1b6ec4dcd1 Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 15:21:27 +0200 Subject: [PATCH 12/22] Update pager_test.gno --- examples/gno.land/p/demo/avl/pager/pager_test.gno | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager_test.gno b/examples/gno.land/p/demo/avl/pager/pager_test.gno index 74ce9b317d4..4cf7446c4cc 100644 --- a/examples/gno.land/p/demo/avl/pager/pager_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -104,8 +104,7 @@ func TestPage_RenderSelector(t *testing.T) { } for _, tt := range tests { - page, err := pager.GetPage(tt.pageNumber, tt.pageSize) - urequire.NoError(t, err) + page := pager.GetPage(tt.pageNumber, tt.pageSize) md := page.RenderSelector() uassert.Equal(t, tt.expected, md) From f1adda04961a7932677895132b51a50fa1588d10 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sun, 14 Jul 2024 15:41:53 +0200 Subject: [PATCH 13/22] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/pager/z_filetest.gno | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/z_filetest.gno b/examples/gno.land/p/demo/avl/pager/z_filetest.gno index dafe44a9c15..00da1aa3537 100644 --- a/examples/gno.land/p/demo/avl/pager/z_filetest.gno +++ b/examples/gno.land/p/demo/avl/pager/z_filetest.gno @@ -19,11 +19,7 @@ func main() { pager := pager.NewPager(tree) for pn := -1; pn < 8; pn++ { - page, err := pager.GetPage(pn, 7) - if err != nil { - println("Error:", err.Error()) - return - } + page := pager.GetPage(pn, 7) // Print the items on the page. println(ufmt.Sprintf("Page %d of %d", page.PageNumber, page.TotalPages)) From c8e4e1fd958f17f8873b6c10d7c79809ea94ccb5 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Tue, 24 Sep 2024 22:29:19 +0200 Subject: [PATCH 14/22] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/pager/pager.gno | 53 ++++--- .../gno.land/p/demo/avl/pager/pager_test.gno | 19 +-- .../gno.land/p/demo/avl/pager/z_filetest.gno | 134 ++++++++---------- 3 files changed, 102 insertions(+), 104 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager.gno b/examples/gno.land/p/demo/avl/pager/pager.gno index 4e6dde41a71..511c35905d5 100644 --- a/examples/gno.land/p/demo/avl/pager/pager.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -13,6 +13,7 @@ import ( type Pager struct { Tree *avl.Tree PageQueryParam string + SizeQueryParam string DefaultPageSize int } @@ -39,6 +40,7 @@ func NewPager(tree *avl.Tree) *Pager { return &Pager{ Tree: tree, PageQueryParam: "page", + SizeQueryParam: "size", DefaultPageSize: 10, } } @@ -115,8 +117,11 @@ func (p *Pager) GetPageByPath(rawURL string) (*Page, error) { return p.GetPage(pageNumber, pageSize), nil } -// RenderSelector generates the Markdown for the page selector. -func (p *Page) RenderSelector() string { +// UI generates the Markdown UI for the page selector. +func (p *Page) Selector() string { + pageNumber := p.PageNumber + pageNumber = max(pageNumber, 1) + if p.TotalPages <= 1 { return "" } @@ -128,9 +133,7 @@ func (p *Page) RenderSelector() string { md += ufmt.Sprintf("[%d](?%s=%d) | ", 1, p.Pager.PageQueryParam, 1) // Before - if p.PageNumber == 5 { - md += ufmt.Sprintf("[%d](?%s=%d) | ", 2, p.Pager.PageQueryParam, 2) - } else if p.PageNumber > 4 { + if p.PageNumber > 4 { md += "… | " } @@ -143,8 +146,12 @@ func (p *Page) RenderSelector() string { } } - // Current page - md += ufmt.Sprintf("**%d**", p.PageNumber) + if p.PageNumber > 0 && p.PageNumber <= p.TotalPages { + // Current page + md += ufmt.Sprintf("**%d**", p.PageNumber) + } else { + md += ufmt.Sprintf("_%d_", p.PageNumber) + } if p.HasNext { md += " | " @@ -157,9 +164,7 @@ func (p *Page) RenderSelector() string { md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+2, p.Pager.PageQueryParam, p.PageNumber+2) } - if p.PageNumber == p.TotalPages-4 { - md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+3, p.Pager.PageQueryParam, p.PageNumber+3) - } else if p.PageNumber < p.TotalPages-3 { + if p.PageNumber < p.TotalPages-3 { md += "… | " } @@ -181,32 +186,36 @@ func (p *Pager) ParseQuery(rawURL string) (int, int, error) { pageNumber := 1 pageSize := p.DefaultPageSize - if pageStr := query.Get(p.PageQueryParam); pageStr != "" { - pageNumber, err = strconv.Atoi(pageStr) - if err != nil || pageNumber < 1 { - pageNumber = 1 + if p.PageQueryParam != "" { + if pageStr := query.Get(p.PageQueryParam); pageStr != "" { + pageNumber, err = strconv.Atoi(pageStr) + if err != nil || pageNumber < 1 { + pageNumber = 1 + } } } - if sizeStr := query.Get("size"); sizeStr != "" { - pageSize, err = strconv.Atoi(sizeStr) - if err != nil || pageSize < 1 { - pageSize = p.DefaultPageSize + if p.SizeQueryParam != "" { + if sizeStr := query.Get(p.SizeQueryParam); sizeStr != "" { + pageSize, err = strconv.Atoi(sizeStr) + if err != nil || pageSize < 1 { + pageSize = p.DefaultPageSize + } } } return pageNumber, pageSize, nil } -func max(a, b int) int { - if a > b { +func min(a, b int) int { + if a < b { return a } return b } -func min(a, b int) int { - if a < b { +func max(a, b int) int { + if a > b { return a } return b diff --git a/examples/gno.land/p/demo/avl/pager/pager_test.gno b/examples/gno.land/p/demo/avl/pager/pager_test.gno index 4cf7446c4cc..abb5a46b280 100644 --- a/examples/gno.land/p/demo/avl/pager/pager_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -80,7 +80,7 @@ func TestPager_GetPageByPath(t *testing.T) { } } -func TestPage_RenderSelector(t *testing.T) { +func TestPage_Selector(t *testing.T) { // Create a new AVL tree and populate it with some key-value pairs. tree := avl.NewTree() tree.Set("a", 1) @@ -106,12 +106,12 @@ func TestPage_RenderSelector(t *testing.T) { for _, tt := range tests { page := pager.GetPage(tt.pageNumber, tt.pageSize) - md := page.RenderSelector() - uassert.Equal(t, tt.expected, md) + ui := page.Selector() + uassert.Equal(t, tt.expected, ui) } } -func TestPager_RenderSelector_WithManyPages(t *testing.T) { +func TestPager_UI_WithManyPages(t *testing.T) { // Create a new AVL tree and populate it with many key-value pairs. tree := avl.NewTree() for i := 0; i < 100; i++ { @@ -127,23 +127,26 @@ func TestPager_RenderSelector_WithManyPages(t *testing.T) { pageSize int expected string }{ + // XXX: -1 + // XXX: 0 {1, 10, "**1** | [2](?page=2) | [3](?page=3) | … | [10](?page=10)"}, {2, 10, "[1](?page=1) | **2** | [3](?page=3) | [4](?page=4) | … | [10](?page=10)"}, {3, 10, "[1](?page=1) | [2](?page=2) | **3** | [4](?page=4) | [5](?page=5) | … | [10](?page=10)"}, {4, 10, "[1](?page=1) | [2](?page=2) | [3](?page=3) | **4** | [5](?page=5) | [6](?page=6) | … | [10](?page=10)"}, - {5, 10, "[1](?page=1) | [2](?page=2) | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) | [7](?page=7) | … | [10](?page=10)"}, - {6, 10, "[1](?page=1) | … | [4](?page=4) | [5](?page=5) | **6** | [7](?page=7) | [8](?page=8) | [9](?page=9) | [10](?page=10)"}, + {5, 10, "[1](?page=1) | … | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) | [7](?page=7) | … | [10](?page=10)"}, + {6, 10, "[1](?page=1) | … | [4](?page=4) | [5](?page=5) | **6** | [7](?page=7) | [8](?page=8) | … | [10](?page=10)"}, {7, 10, "[1](?page=1) | … | [5](?page=5) | [6](?page=6) | **7** | [8](?page=8) | [9](?page=9) | [10](?page=10)"}, {8, 10, "[1](?page=1) | … | [6](?page=6) | [7](?page=7) | **8** | [9](?page=9) | [10](?page=10)"}, {9, 10, "[1](?page=1) | … | [7](?page=7) | [8](?page=8) | **9** | [10](?page=10)"}, {10, 10, "[1](?page=1) | … | [8](?page=8) | [9](?page=9) | **10**"}, + // XXX: 11 } for _, tt := range tests { page := pager.GetPage(tt.pageNumber, tt.pageSize) - md := page.RenderSelector() - uassert.Equal(t, tt.expected, md) + ui := page.Selector() + uassert.Equal(t, tt.expected, ui) } } diff --git a/examples/gno.land/p/demo/avl/pager/z_filetest.gno b/examples/gno.land/p/demo/avl/pager/z_filetest.gno index 00da1aa3537..e531e5557dd 100644 --- a/examples/gno.land/p/demo/avl/pager/z_filetest.gno +++ b/examples/gno.land/p/demo/avl/pager/z_filetest.gno @@ -21,95 +21,81 @@ func main() { for pn := -1; pn < 8; pn++ { page := pager.GetPage(pn, 7) - // Print the items on the page. - println(ufmt.Sprintf("Page %d of %d", page.PageNumber, page.TotalPages)) - for _, item := range page.Items { - println(ufmt.Sprintf("Key: %s, Value: %v", item.Key, item.Value)) + println(ufmt.Sprintf("## Page %d of %d", page.PageNumber, page.TotalPages)) + for idx, item := range page.Items { + println(ufmt.Sprintf("- idx=%d key=%s value=%d", idx, item.Key, item.Value)) } - - // Render and print the page selector. - pageSelector := page.RenderSelector() - println("Page Selector:") - println(pageSelector) + println(page.Selector()) println() } } // Output: -// Page 0 of 6 -// Page Selector: -// **0** | [1](?page=1) | [2](?page=2) | … | [6](?page=6) +// ## Page 0 of 6 +// _0_ | [1](?page=1) | [2](?page=2) | … | [6](?page=6) // -// Page 0 of 6 -// Page Selector: -// **0** | [1](?page=1) | [2](?page=2) | … | [6](?page=6) +// ## Page 0 of 6 +// _0_ | [1](?page=1) | [2](?page=2) | … | [6](?page=6) // -// Page 1 of 6 -// Key: 0000001, Value: (unhandled) -// Key: 0000002, Value: (unhandled) -// Key: 0000003, Value: (unhandled) -// Key: 0000004, Value: (unhandled) -// Key: 0000005, Value: (unhandled) -// Key: 0000006, Value: (unhandled) -// Key: 0000007, Value: (unhandled) -// Page Selector: +// ## Page 1 of 6 +// - idx=0 key=0000001 value=0 +// - idx=1 key=0000002 value=1 +// - idx=2 key=0000003 value=2 +// - idx=3 key=0000004 value=3 +// - idx=4 key=0000005 value=4 +// - idx=5 key=0000006 value=5 +// - idx=6 key=0000007 value=6 // **1** | [2](?page=2) | [3](?page=3) | … | [6](?page=6) // -// Page 2 of 6 -// Key: 0000008, Value: (unhandled) -// Key: 0000009, Value: (unhandled) -// Key: 000000a, Value: (unhandled) -// Key: 000000b, Value: (unhandled) -// Key: 000000c, Value: (unhandled) -// Key: 000000d, Value: (unhandled) -// Key: 000000e, Value: (unhandled) -// Page Selector: -// [1](?page=1) | **2** | [3](?page=3) | [4](?page=4) | [5](?page=5) | [6](?page=6) +// ## Page 2 of 6 +// - idx=0 key=0000008 value=7 +// - idx=1 key=0000009 value=8 +// - idx=2 key=000000a value=9 +// - idx=3 key=000000b value=10 +// - idx=4 key=000000c value=11 +// - idx=5 key=000000d value=12 +// - idx=6 key=000000e value=13 +// [1](?page=1) | **2** | [3](?page=3) | [4](?page=4) | … | [6](?page=6) // -// Page 3 of 6 -// Key: 000000f, Value: (unhandled) -// Key: 000000g, Value: (unhandled) -// Key: 000000h, Value: (unhandled) -// Key: 000000j, Value: (unhandled) -// Key: 000000k, Value: (unhandled) -// Key: 000000m, Value: (unhandled) -// Key: 000000n, Value: (unhandled) -// Page Selector: +// ## Page 3 of 6 +// - idx=0 key=000000f value=14 +// - idx=1 key=000000g value=15 +// - idx=2 key=000000h value=16 +// - idx=3 key=000000j value=17 +// - idx=4 key=000000k value=18 +// - idx=5 key=000000m value=19 +// - idx=6 key=000000n value=20 // [1](?page=1) | [2](?page=2) | **3** | [4](?page=4) | [5](?page=5) | [6](?page=6) // -// Page 4 of 6 -// Key: 000000p, Value: (unhandled) -// Key: 000000q, Value: (unhandled) -// Key: 000000r, Value: (unhandled) -// Key: 000000s, Value: (unhandled) -// Key: 000000t, Value: (unhandled) -// Key: 000000v, Value: (unhandled) -// Key: 000000w, Value: (unhandled) -// Page Selector: +// ## Page 4 of 6 +// - idx=0 key=000000p value=21 +// - idx=1 key=000000q value=22 +// - idx=2 key=000000r value=23 +// - idx=3 key=000000s value=24 +// - idx=4 key=000000t value=25 +// - idx=5 key=000000v value=26 +// - idx=6 key=000000w value=27 // [1](?page=1) | [2](?page=2) | [3](?page=3) | **4** | [5](?page=5) | [6](?page=6) // -// Page 5 of 6 -// Key: 000000x, Value: (unhandled) -// Key: 000000y, Value: (unhandled) -// Key: 000000z, Value: (unhandled) -// Key: 0000010, Value: (unhandled) -// Key: 0000011, Value: (unhandled) -// Key: 0000012, Value: (unhandled) -// Key: 0000013, Value: (unhandled) -// Page Selector: -// [1](?page=1) | [2](?page=2) | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) +// ## Page 5 of 6 +// - idx=0 key=000000x value=28 +// - idx=1 key=000000y value=29 +// - idx=2 key=000000z value=30 +// - idx=3 key=0000010 value=31 +// - idx=4 key=0000011 value=32 +// - idx=5 key=0000012 value=33 +// - idx=6 key=0000013 value=34 +// [1](?page=1) | … | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) // -// Page 6 of 6 -// Key: 0000014, Value: (unhandled) -// Key: 0000015, Value: (unhandled) -// Key: 0000016, Value: (unhandled) -// Key: 0000017, Value: (unhandled) -// Key: 0000018, Value: (unhandled) -// Key: 0000019, Value: (unhandled) -// Key: 000001a, Value: (unhandled) -// Page Selector: +// ## Page 6 of 6 +// - idx=0 key=0000014 value=35 +// - idx=1 key=0000015 value=36 +// - idx=2 key=0000016 value=37 +// - idx=3 key=0000017 value=38 +// - idx=4 key=0000018 value=39 +// - idx=5 key=0000019 value=40 +// - idx=6 key=000001a value=41 // [1](?page=1) | … | [4](?page=4) | [5](?page=5) | **6** // -// Page 7 of 6 -// Page Selector: -// [1](?page=1) | … | [5](?page=5) | [6](?page=6) | **7** +// ## Page 7 of 6 +// [1](?page=1) | … | [5](?page=5) | [6](?page=6) | _7_ From a0be0358e20889c994cca58fffe1fb85427cee8b Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Tue, 24 Sep 2024 23:07:30 +0200 Subject: [PATCH 15/22] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/r/demo/users/users.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/r/demo/users/users.gno b/examples/gno.land/r/demo/users/users.gno index 11eff33d72c..baf50f68927 100644 --- a/examples/gno.land/r/demo/users/users.gno +++ b/examples/gno.land/r/demo/users/users.gno @@ -336,7 +336,7 @@ func renderHome(path string) string { doc += " * [" + user.Name + "](/r/demo/users:" + user.Name + ")\n" } - doc += page.RenderSelector() + doc += page.Selector() return doc } From 5c3524dca4d8187600e6c70ed9b2afdd009de156 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Tue, 24 Sep 2024 23:15:35 +0200 Subject: [PATCH 16/22] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/r/demo/users/users.gno | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/gno.land/r/demo/users/users.gno b/examples/gno.land/r/demo/users/users.gno index baf50f68927..652957fb6c0 100644 --- a/examples/gno.land/r/demo/users/users.gno +++ b/examples/gno.land/r/demo/users/users.gno @@ -328,8 +328,7 @@ func Render(fullPath string) string { func renderHome(path string) string { doc := "" - p := pager.NewPager(&name2User) - page := p.MustGetPageByPath(path) + page := pager.NewPager(&name2User).MustGetPageByPath(path) for _, item := range page.Items { user := item.Value.(*users.User) From 1da88059f98f762eb6e57e142fdcab0dfef83b36 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Thu, 26 Sep 2024 21:16:21 +0200 Subject: [PATCH 17/22] chore: new API Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/pager/pager.gno | 25 ++++++++++++++++--- .../gno.land/p/demo/avl/pager/pager_test.gno | 16 ++++++------ .../gno.land/p/demo/avl/pager/z_filetest.gno | 4 +-- examples/gno.land/r/demo/users/users.gno | 2 +- .../gno.land/r/demo/users/z_5_filetest.gno | 6 ++--- 5 files changed, 35 insertions(+), 18 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager.gno b/examples/gno.land/p/demo/avl/pager/pager.gno index 511c35905d5..fb2e979c51b 100644 --- a/examples/gno.land/p/demo/avl/pager/pager.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -36,20 +36,37 @@ type Item struct { } // NewPager creates a new Pager with default values. -func NewPager(tree *avl.Tree) *Pager { +func NewPager(tree *avl.Tree, defaultPageSize int) *Pager { return &Pager{ Tree: tree, PageQueryParam: "page", SizeQueryParam: "size", - DefaultPageSize: 10, + DefaultPageSize: defaultPageSize, } } // GetPage retrieves a page of results from the AVL tree. -func (p *Pager) GetPage(pageNumber int, pageSize int) *Page { +func (p *Pager) GetPage(pageNumber int) *Page { + return p.GetPageWithSize(pageNumber, p.DefaultPageSize) +} + +func (p *Pager) GetPageWithSize(pageNumber, pageSize int) *Page { totalItems := p.Tree.Size() totalPages := int(math.Ceil(float64(totalItems) / float64(pageSize))) + if pageSize < 1 { + return &Page{ + Items: []Item{}, + PageNumber: 0, + PageSize: 0, + TotalItems: totalItems, + TotalPages: totalPages, + HasPrev: false, + HasNext: false, + Pager: p, + } + } + if pageNumber < 1 { return &Page{ Items: []Item{}, @@ -114,7 +131,7 @@ func (p *Pager) GetPageByPath(rawURL string) (*Page, error) { if err != nil { return nil, err } - return p.GetPage(pageNumber, pageSize), nil + return p.GetPageWithSize(pageNumber, pageSize), nil } // UI generates the Markdown UI for the page selector. diff --git a/examples/gno.land/p/demo/avl/pager/pager_test.gno b/examples/gno.land/p/demo/avl/pager/pager_test.gno index abb5a46b280..da4680db8c7 100644 --- a/examples/gno.land/p/demo/avl/pager/pager_test.gno +++ b/examples/gno.land/p/demo/avl/pager/pager_test.gno @@ -19,7 +19,7 @@ func TestPager_GetPage(t *testing.T) { tree.Set("e", 5) // Create a new pager. - pager := NewPager(tree) + pager := NewPager(tree, 10) // Define test cases. tests := []struct { @@ -37,7 +37,7 @@ func TestPager_GetPage(t *testing.T) { } for _, tt := range tests { - page := pager.GetPage(tt.pageNumber, tt.pageSize) + page := pager.GetPageWithSize(tt.pageNumber, tt.pageSize) uassert.Equal(t, len(tt.expected), len(page.Items)) @@ -56,7 +56,7 @@ func TestPager_GetPageByPath(t *testing.T) { } // Create a new pager. - pager := NewPager(tree) + pager := NewPager(tree, 10) // Define test cases. tests := []struct { @@ -90,7 +90,7 @@ func TestPage_Selector(t *testing.T) { tree.Set("e", 5) // Create a new pager. - pager := NewPager(tree) + pager := NewPager(tree, 10) // Define test cases. tests := []struct { @@ -104,7 +104,7 @@ func TestPage_Selector(t *testing.T) { } for _, tt := range tests { - page := pager.GetPage(tt.pageNumber, tt.pageSize) + page := pager.GetPageWithSize(tt.pageNumber, tt.pageSize) ui := page.Selector() uassert.Equal(t, tt.expected, ui) @@ -119,7 +119,7 @@ func TestPager_UI_WithManyPages(t *testing.T) { } // Create a new pager. - pager := NewPager(tree) + pager := NewPager(tree, 10) // Define test cases for a large number of pages. tests := []struct { @@ -143,7 +143,7 @@ func TestPager_UI_WithManyPages(t *testing.T) { } for _, tt := range tests { - page := pager.GetPage(tt.pageNumber, tt.pageSize) + page := pager.GetPageWithSize(tt.pageNumber, tt.pageSize) ui := page.Selector() uassert.Equal(t, tt.expected, ui) @@ -160,7 +160,7 @@ func TestPager_ParseQuery(t *testing.T) { tree.Set("e", 5) // Create a new pager. - pager := NewPager(tree) + pager := NewPager(tree, 10) // Define test cases. tests := []struct { diff --git a/examples/gno.land/p/demo/avl/pager/z_filetest.gno b/examples/gno.land/p/demo/avl/pager/z_filetest.gno index e531e5557dd..91c20115469 100644 --- a/examples/gno.land/p/demo/avl/pager/z_filetest.gno +++ b/examples/gno.land/p/demo/avl/pager/z_filetest.gno @@ -16,10 +16,10 @@ func main() { } // Create a new pager. - pager := pager.NewPager(tree) + pager := pager.NewPager(tree, 7) for pn := -1; pn < 8; pn++ { - page := pager.GetPage(pn, 7) + page := pager.GetPage(pn) println(ufmt.Sprintf("## Page %d of %d", page.PageNumber, page.TotalPages)) for idx, item := range page.Items { diff --git a/examples/gno.land/r/demo/users/users.gno b/examples/gno.land/r/demo/users/users.gno index 62b422558db..2655d299ef7 100644 --- a/examples/gno.land/r/demo/users/users.gno +++ b/examples/gno.land/r/demo/users/users.gno @@ -328,7 +328,7 @@ func Render(fullPath string) string { func renderHome(path string) string { doc := "" - page := pager.NewPager(&name2User).MustGetPageByPath(path) + page := pager.NewPager(&name2User, 50).MustGetPageByPath(path) for _, item := range page.Items { user := item.Value.(*users.User) diff --git a/examples/gno.land/r/demo/users/z_5_filetest.gno b/examples/gno.land/r/demo/users/z_5_filetest.gno index c3bbce2bde5..df1949ed6d0 100644 --- a/examples/gno.land/r/demo/users/z_5_filetest.gno +++ b/examples/gno.land/r/demo/users/z_5_filetest.gno @@ -48,11 +48,11 @@ func main() { // * [nt](/r/demo/users:nt) // * [satoshi](/r/demo/users:satoshi) // * [sys](/r/demo/users:sys) -// **1** | [2](?page=2) -// ======================================== // * [test1](/r/demo/users:test1) // * [x](/r/demo/users:x) -// [1](?page=1) | **2** +// +// ======================================== +// // ======================================== // ## user gnouser // From 1c107329eaf5913e6bd9006ca9bf800d12bfe25b Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Sat, 5 Oct 2024 12:51:45 +0200 Subject: [PATCH 18/22] Update examples/gno.land/p/demo/avl/pager/pager.gno Co-authored-by: Antonio Navarro Perez --- examples/gno.land/p/demo/avl/pager/pager.gno | 42 +++++++------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager.gno b/examples/gno.land/p/demo/avl/pager/pager.gno index fb2e979c51b..6c9c23d8e3b 100644 --- a/examples/gno.land/p/demo/avl/pager/pager.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -54,43 +54,31 @@ func (p *Pager) GetPageWithSize(pageNumber, pageSize int) *Page { totalItems := p.Tree.Size() totalPages := int(math.Ceil(float64(totalItems) / float64(pageSize))) - if pageSize < 1 { - return &Page{ - Items: []Item{}, - PageNumber: 0, - PageSize: 0, + page := &Page{ TotalItems: totalItems, TotalPages: totalPages, - HasPrev: false, - HasNext: false, + PageSize: pageSize, Pager: p, } + + // pages without content + if pageSize < 1 { + return page } + // page number provided is not available if pageNumber < 1 { - return &Page{ - Items: []Item{}, - PageNumber: 0, - PageSize: pageSize, - TotalItems: totalItems, - TotalPages: totalPages, - HasPrev: false, - HasNext: pageNumber < totalPages, - Pager: p, - } + page.HasNext = pageNumber < totalPages + + return page } + // page number provided is outside the range of total pages if pageNumber > totalPages { - return &Page{ - Items: []Item{}, - PageNumber: pageNumber, - PageSize: pageSize, - TotalItems: totalItems, - TotalPages: totalPages, - HasPrev: totalPages > 0, - HasNext: false, - Pager: p, - } + page.PageNumber = pageNumber + page.HasPrev = totalPages > 0 + + return page } startIndex := (pageNumber - 1) * pageSize From 79ffd7db24c480be1f0cdf2ea44b0c8d106a6073 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sat, 5 Oct 2024 12:56:38 +0200 Subject: [PATCH 19/22] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/pager/pager.gno | 31 ++++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager.gno b/examples/gno.land/p/demo/avl/pager/pager.gno index 6c9c23d8e3b..5e79aac8458 100644 --- a/examples/gno.land/p/demo/avl/pager/pager.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -55,11 +55,11 @@ func (p *Pager) GetPageWithSize(pageNumber, pageSize int) *Page { totalPages := int(math.Ceil(float64(totalItems) / float64(pageSize))) page := &Page{ - TotalItems: totalItems, - TotalPages: totalPages, - PageSize: pageSize, - Pager: p, - } + TotalItems: totalItems, + TotalPages: totalPages, + PageSize: pageSize, + Pager: p, + } // pages without content if pageSize < 1 { @@ -68,16 +68,14 @@ func (p *Pager) GetPageWithSize(pageNumber, pageSize int) *Page { // page number provided is not available if pageNumber < 1 { - page.HasNext = pageNumber < totalPages - + page.HasNext = totalPages > 0 return page } // page number provided is outside the range of total pages if pageNumber > totalPages { page.PageNumber = pageNumber - page.HasPrev = totalPages > 0 - + page.HasPrev = pageNumber > 0 return page } @@ -93,16 +91,11 @@ func (p *Pager) GetPageWithSize(pageNumber, pageSize int) *Page { return false }) - return &Page{ - Items: items, - PageNumber: pageNumber, - PageSize: pageSize, - TotalItems: totalItems, - TotalPages: totalPages, - HasPrev: pageNumber > 1, - HasNext: pageNumber < totalPages, - Pager: p, - } + page.Items = items + page.PageNumber = pageNumber + page.HasPrev = pageNumber > 1 + page.HasNext = pageNumber < totalPages + return page } func (p *Pager) MustGetPageByPath(rawURL string) *Page { From 3754a7093a6f658d6be474340f03e0cd44964c34 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Sat, 5 Oct 2024 22:33:58 +0200 Subject: [PATCH 20/22] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/pager/pager.gno | 7 ------- 1 file changed, 7 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager.gno b/examples/gno.land/p/demo/avl/pager/pager.gno index 5e79aac8458..60bb44d97b6 100644 --- a/examples/gno.land/p/demo/avl/pager/pager.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -205,13 +205,6 @@ func (p *Pager) ParseQuery(rawURL string) (int, int, error) { return pageNumber, pageSize, nil } -func min(a, b int) int { - if a < b { - return a - } - return b -} - func max(a, b int) int { if a > b { return a From b9b7209cdcd9f923f2d053ddbde63a85736c32db Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Tue, 8 Oct 2024 16:13:52 +0200 Subject: [PATCH 21/22] Update examples/gno.land/r/demo/users/users.gno Co-authored-by: Leon Hudak <33522493+leohhhn@users.noreply.github.com> --- examples/gno.land/r/demo/users/users.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/r/demo/users/users.gno b/examples/gno.land/r/demo/users/users.gno index 46ce3945bcd..95ad41ba500 100644 --- a/examples/gno.land/r/demo/users/users.gno +++ b/examples/gno.land/r/demo/users/users.gno @@ -334,7 +334,7 @@ func renderHome(path string) string { user := item.Value.(*users.User) doc += " * [" + user.Name + "](/r/demo/users:" + user.Name + ")\n" } - +doc += "\n" doc += page.Selector() return doc } From fbc75c8cb2456d30e97353c119e3c68d2e037ecf Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Wed, 6 Nov 2024 19:32:24 +0100 Subject: [PATCH 22/22] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/r/demo/users/users.gno | 2 +- examples/gno.land/r/demo/users/z_5_filetest.gno | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/gno.land/r/demo/users/users.gno b/examples/gno.land/r/demo/users/users.gno index 95ad41ba500..daad2e7fc18 100644 --- a/examples/gno.land/r/demo/users/users.gno +++ b/examples/gno.land/r/demo/users/users.gno @@ -334,7 +334,7 @@ func renderHome(path string) string { user := item.Value.(*users.User) doc += " * [" + user.Name + "](/r/demo/users:" + user.Name + ")\n" } -doc += "\n" + doc += "\n" doc += page.Selector() return doc } diff --git a/examples/gno.land/r/demo/users/z_5_filetest.gno b/examples/gno.land/r/demo/users/z_5_filetest.gno index ee213c3c73f..2b3e1b17b5c 100644 --- a/examples/gno.land/r/demo/users/z_5_filetest.gno +++ b/examples/gno.land/r/demo/users/z_5_filetest.gno @@ -51,8 +51,10 @@ func main() { // * [test1](/r/demo/users:test1) // * [x](/r/demo/users:x) // +// // ======================================== // +// // ======================================== // ## user gnouser //