228 lines
4.6 KiB
Go
228 lines
4.6 KiB
Go
|
// GoToSocial
|
||
|
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||
|
//
|
||
|
// This program is free software: you can redistribute it and/or modify
|
||
|
// it under the terms of the GNU Affero General Public License as published by
|
||
|
// the Free Software Foundation, either version 3 of the License, or
|
||
|
// (at your option) any later version.
|
||
|
//
|
||
|
// This program is distributed in the hope that it will be useful,
|
||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
// GNU Affero General Public License for more details.
|
||
|
//
|
||
|
// You should have received a copy of the GNU Affero General Public License
|
||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
package paging
|
||
|
|
||
|
import "golang.org/x/exp/slices"
|
||
|
|
||
|
// Pager provides a means of paging serialized IDs,
|
||
|
// using the terminology of our API endpoint queries.
|
||
|
type Pager struct {
|
||
|
// SinceID will limit the returned
|
||
|
// page of IDs to contain newer than
|
||
|
// since ID (excluding it). Result
|
||
|
// will be returned DESCENDING.
|
||
|
SinceID string
|
||
|
|
||
|
// MinID will limit the returned
|
||
|
// page of IDs to contain newer than
|
||
|
// min ID (excluding it). Result
|
||
|
// will be returned ASCENDING.
|
||
|
MinID string
|
||
|
|
||
|
// MaxID will limit the returned
|
||
|
// page of IDs to contain older
|
||
|
// than (excluding) this max ID.
|
||
|
MaxID string
|
||
|
|
||
|
// Limit will limit the returned
|
||
|
// page of IDs to at most 'limit'.
|
||
|
Limit int
|
||
|
}
|
||
|
|
||
|
// Page will page the given slice of GoToSocial IDs according
|
||
|
// to the receiving Pager's SinceID, MinID, MaxID and Limits.
|
||
|
// NOTE THE INPUT SLICE MUST BE SORTED IN ASCENDING ORDER
|
||
|
// (I.E. OLDEST ITEMS AT LOWEST INDICES, NEWER AT HIGHER).
|
||
|
func (p *Pager) PageAsc(ids []string) []string {
|
||
|
if p == nil {
|
||
|
// no paging.
|
||
|
return ids
|
||
|
}
|
||
|
|
||
|
var asc bool
|
||
|
|
||
|
if p.SinceID != "" {
|
||
|
// If a sinceID is given, we
|
||
|
// page down i.e. descending.
|
||
|
asc = false
|
||
|
|
||
|
for i := 0; i < len(ids); i++ {
|
||
|
if ids[i] == p.SinceID {
|
||
|
// Hit the boundary.
|
||
|
// Reslice to be:
|
||
|
// "from here"
|
||
|
ids = ids[i+1:]
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
} else if p.MinID != "" {
|
||
|
// We only support minID if
|
||
|
// no sinceID is provided.
|
||
|
//
|
||
|
// If a minID is given, we
|
||
|
// page up, i.e. ascending.
|
||
|
asc = true
|
||
|
|
||
|
for i := 0; i < len(ids); i++ {
|
||
|
if ids[i] == p.MinID {
|
||
|
// Hit the boundary.
|
||
|
// Reslice to be:
|
||
|
// "from here"
|
||
|
ids = ids[i+1:]
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if p.MaxID != "" {
|
||
|
for i := 0; i < len(ids); i++ {
|
||
|
if ids[i] == p.MaxID {
|
||
|
// Hit the boundary.
|
||
|
// Reslice to be:
|
||
|
// "up to here"
|
||
|
ids = ids[:i]
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if !asc && len(ids) > 1 {
|
||
|
var (
|
||
|
// Start at front.
|
||
|
i = 0
|
||
|
|
||
|
// Start at back.
|
||
|
j = len(ids) - 1
|
||
|
)
|
||
|
|
||
|
// Clone input IDs before
|
||
|
// we perform modifications.
|
||
|
ids = slices.Clone(ids)
|
||
|
|
||
|
for i < j {
|
||
|
// Swap i,j index values in slice.
|
||
|
ids[i], ids[j] = ids[j], ids[i]
|
||
|
|
||
|
// incr + decr,
|
||
|
// looping until
|
||
|
// they meet in
|
||
|
// the middle.
|
||
|
i++
|
||
|
j--
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if p.Limit > 0 && p.Limit < len(ids) {
|
||
|
// Reslice IDs to given limit.
|
||
|
ids = ids[:p.Limit]
|
||
|
}
|
||
|
|
||
|
return ids
|
||
|
}
|
||
|
|
||
|
// Page will page the given slice of GoToSocial IDs according
|
||
|
// to the receiving Pager's SinceID, MinID, MaxID and Limits.
|
||
|
// NOTE THE INPUT SLICE MUST BE SORTED IN ASCENDING ORDER.
|
||
|
// (I.E. NEWEST ITEMS AT LOWEST INDICES, OLDER AT HIGHER).
|
||
|
func (p *Pager) PageDesc(ids []string) []string {
|
||
|
if p == nil {
|
||
|
// no paging.
|
||
|
return ids
|
||
|
}
|
||
|
|
||
|
var asc bool
|
||
|
|
||
|
if p.MaxID != "" {
|
||
|
for i := 0; i < len(ids); i++ {
|
||
|
if ids[i] == p.MaxID {
|
||
|
// Hit the boundary.
|
||
|
// Reslice to be:
|
||
|
// "from here"
|
||
|
ids = ids[i+1:]
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if p.SinceID != "" {
|
||
|
// If a sinceID is given, we
|
||
|
// page down i.e. descending.
|
||
|
asc = false
|
||
|
|
||
|
for i := 0; i < len(ids); i++ {
|
||
|
if ids[i] == p.SinceID {
|
||
|
// Hit the boundary.
|
||
|
// Reslice to be:
|
||
|
// "up to here"
|
||
|
ids = ids[:i]
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
} else if p.MinID != "" {
|
||
|
// We only support minID if
|
||
|
// no sinceID is provided.
|
||
|
//
|
||
|
// If a minID is given, we
|
||
|
// page up, i.e. ascending.
|
||
|
asc = true
|
||
|
|
||
|
for i := 0; i < len(ids); i++ {
|
||
|
if ids[i] == p.MinID {
|
||
|
// Hit the boundary.
|
||
|
// Reslice to be:
|
||
|
// "up to here"
|
||
|
ids = ids[:i]
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if asc && len(ids) > 1 {
|
||
|
var (
|
||
|
// Start at front.
|
||
|
i = 0
|
||
|
|
||
|
// Start at back.
|
||
|
j = len(ids) - 1
|
||
|
)
|
||
|
|
||
|
// Clone input IDs before
|
||
|
// we perform modifications.
|
||
|
ids = slices.Clone(ids)
|
||
|
|
||
|
for i < j {
|
||
|
// Swap i,j index values in slice.
|
||
|
ids[i], ids[j] = ids[j], ids[i]
|
||
|
|
||
|
// incr + decr,
|
||
|
// looping until
|
||
|
// they meet in
|
||
|
// the middle.
|
||
|
i++
|
||
|
j--
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if p.Limit > 0 && p.Limit < len(ids) {
|
||
|
// Reslice IDs to given limit.
|
||
|
ids = ids[:p.Limit]
|
||
|
}
|
||
|
|
||
|
return ids
|
||
|
}
|