Skip to content

Commit

Permalink
Do not assume a consistent order when paginating
Browse files Browse the repository at this point in the history
  • Loading branch information
BartArys committed Jul 22, 2020
1 parent 359fc1a commit bc05559
Showing 1 changed file with 28 additions and 6 deletions.
34 changes: 28 additions & 6 deletions core/src/main/kotlin/com/gitlab/kordlib/core/Util.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import kotlinx.atomicfu.atomic
import kotlinx.coroutines.flow.*
import java.time.Instant
import java.time.format.DateTimeFormatter
import kotlin.math.max
import kotlin.reflect.KClass

internal fun String?.toSnowflakeOrNull(): Snowflake? = when {
Expand Down Expand Up @@ -110,37 +111,58 @@ internal fun <C : Collection<T>, T> paginate(
* Discord returns values in order newest -> oldest (big -> small) (confirmed for messages),
* meaning that the first item returned is the one last created (youngest) in the batch.
*/
internal fun <T> youngestItem(): (Collection<T>) -> T? = { it.firstOrNull() }
internal fun <T> youngestItem(idSelector: (T) -> String): (Collection<T>) -> T? = function@{
if (it.size <= 1) return@function it.firstOrNull()

val first = it.first()
val last = it.last()

val firstId = idSelector(first).toLong()
val lastId = idSelector(last).toLong()

if (firstId > lastId) first
else last
}

/**
* Discord returns values in order oldest -> newest (big -> small) (confirmed for messages),
* meaning that the last item returned is the one first created (oldest) in the batch.
*/
internal fun <T> oldestItem(): (Collection<T>) -> T? = { it.lastOrNull() }
internal fun <T> oldestItem(idSelector: (T) -> String): (Collection<T>) -> T? = function@{
if (it.size <= 1) return@function it.firstOrNull()
val first = it.first()
val last = it.last()

val firstId = idSelector(first).toLong()
val lastId = idSelector(last).toLong()

if (firstId < lastId) first
else last
}

/**
* Selects the [Position.After] the youngest item in the batch.
*/
internal fun <C : Collection<T>, T> paginateForwards(start: Snowflake = Snowflake("0"), batchSize: Int, idSelector: (T) -> String, request: suspend (position: Position) -> C): Flow<T> =
paginate(start.value, batchSize, idSelector, youngestItem(), Position::After, request)
paginate(start.value, batchSize, idSelector, youngestItem(idSelector), Position::After, request)

/**
* Selects the [Position.After] the youngest item in the batch.
*/
internal fun <C : Collection<T>, T : Entity> paginateForwards(start: Snowflake = Snowflake("0"), batchSize: Int, request: suspend (position: Position) -> C): Flow<T> =
paginate(start.value, batchSize, { it.id.value }, youngestItem(), Position::After, request)
paginate(start.value, batchSize, { it.id.value }, youngestItem { it.id.value }, Position::After, request)

/**
* Selects the [Position.Before] the oldest item in the batch.
*/
internal fun <C : Collection<T>, T> paginateBackwards(start: Snowflake = Snowflake(Long.MAX_VALUE), batchSize: Int, idSelector: (T) -> String, request: suspend (position: Position) -> C): Flow<T> =
paginate(start.value, batchSize, idSelector, oldestItem(), Position::Before, request)
paginate(start.value, batchSize, idSelector, oldestItem(idSelector), Position::Before, request)

/**
* Selects the [Position.Before] the oldest item in the batch.
*/
internal fun <C : Collection<T>, T : Entity> paginateBackwards(start: Snowflake = Snowflake(Long.MAX_VALUE), batchSize: Int, request: suspend (position: Position) -> C): Flow<T> =
paginate(start.value, batchSize, { it.id.value }, oldestItem(), Position::Before, request)
paginate(start.value, batchSize, { it.id.value }, oldestItem { it.id.value }, Position::Before, request)

inline fun <reified T : Event> Intents.IntentsBuilder.enableEvent() = enableEvent(T::class)

Expand Down

0 comments on commit bc05559

Please sign in to comment.