github.com/icyphox/x@v0.0.355-0.20220311094250-029bd783e8b8/pagination/pagepagination/pagination.go (about)

     1  package pagepagination
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"net/url"
     7  	"strconv"
     8  	"strings"
     9  )
    10  
    11  type PagePaginator struct {
    12  	MaxItems     int
    13  	DefaultItems int
    14  }
    15  
    16  func (p *PagePaginator) defaults() {
    17  	if p.MaxItems == 0 {
    18  		p.MaxItems = 1000
    19  	}
    20  
    21  	if p.DefaultItems == 0 {
    22  		p.DefaultItems = 250
    23  	}
    24  }
    25  
    26  // ParsePagination parses limit and page from *http.Request with given limits and defaults.
    27  func (p *PagePaginator) ParsePagination(r *http.Request) (page, itemsPerPage int) {
    28  	p.defaults()
    29  
    30  	if offsetParam := r.URL.Query().Get("page"); offsetParam == "" {
    31  		page = 0
    32  	} else {
    33  		if offset, err := strconv.ParseInt(offsetParam, 10, 0); err != nil {
    34  			page = 0
    35  		} else {
    36  			page = int(offset)
    37  		}
    38  	}
    39  
    40  	if limitParam := r.URL.Query().Get("per_page"); limitParam == "" {
    41  		itemsPerPage = p.DefaultItems
    42  	} else {
    43  		if limit, err := strconv.ParseInt(limitParam, 10, 0); err != nil {
    44  			itemsPerPage = p.DefaultItems
    45  		} else {
    46  			itemsPerPage = int(limit)
    47  		}
    48  	}
    49  
    50  	if itemsPerPage > p.MaxItems {
    51  		itemsPerPage = p.MaxItems
    52  	}
    53  
    54  	if itemsPerPage < 1 {
    55  		itemsPerPage = 1
    56  	}
    57  
    58  	if page < 0 {
    59  		page = 0
    60  	}
    61  
    62  	return
    63  }
    64  
    65  func header(u *url.URL, rel string, limit, page int64) string {
    66  	q := u.Query()
    67  	q.Set("per_page", fmt.Sprintf("%d", limit))
    68  	q.Set("page", fmt.Sprintf("%d", page/limit))
    69  	u.RawQuery = q.Encode()
    70  	return fmt.Sprintf("<%s>; rel=\"%s\"", u.String(), rel)
    71  }
    72  
    73  func PaginationHeader(w http.ResponseWriter, u *url.URL, total int64, page, itemsPerPage int) {
    74  	if itemsPerPage <= 0 {
    75  		itemsPerPage = 1
    76  	}
    77  
    78  	itemsPerPage64 := int64(itemsPerPage)
    79  	offset := int64(page) * itemsPerPage64
    80  
    81  	// lastOffset will either equal the offset required to contain the remainder,
    82  	// or the limit.
    83  	var lastOffset int64
    84  	if total%itemsPerPage64 == 0 {
    85  		lastOffset = total - itemsPerPage64
    86  	} else {
    87  		lastOffset = (total / itemsPerPage64) * itemsPerPage64
    88  	}
    89  
    90  	w.Header().Set("X-Total-Count", strconv.FormatInt(total, 10))
    91  
    92  	// Check for last page
    93  	if offset >= lastOffset {
    94  		if total == 0 {
    95  			w.Header().Set("Link", strings.Join([]string{
    96  				header(u, "first", itemsPerPage64, 0),
    97  				header(u, "next", itemsPerPage64, ((offset/itemsPerPage64)+1)*itemsPerPage64),
    98  				header(u, "prev", itemsPerPage64, ((offset/itemsPerPage64)-1)*itemsPerPage64),
    99  			}, ","))
   100  			return
   101  		}
   102  
   103  		if total < itemsPerPage64 {
   104  			w.Header().Set("link", header(u, "first", total, 0))
   105  			return
   106  		}
   107  
   108  		w.Header().Set("Link", strings.Join([]string{
   109  			header(u, "first", itemsPerPage64, 0),
   110  			header(u, "prev", itemsPerPage64, lastOffset-itemsPerPage64),
   111  		}, ","))
   112  		return
   113  	}
   114  
   115  	if offset < itemsPerPage64 {
   116  		w.Header().Set("Link", strings.Join([]string{
   117  			header(u, "next", itemsPerPage64, itemsPerPage64),
   118  			header(u, "last", itemsPerPage64, lastOffset),
   119  		}, ","))
   120  		return
   121  	}
   122  
   123  	w.Header().Set("Link", strings.Join([]string{
   124  		header(u, "first", itemsPerPage64, 0),
   125  		header(u, "next", itemsPerPage64, ((offset/itemsPerPage64)+1)*itemsPerPage64),
   126  		header(u, "prev", itemsPerPage64, ((offset/itemsPerPage64)-1)*itemsPerPage64),
   127  		header(u, "last", itemsPerPage64, lastOffset),
   128  	}, ","))
   129  }