use std::sync::Arc;
use async_graphql::connection::Edge;
use crossbeam_channel::Sender;
use koan_core::db::connection::Database;
use koan_core::db::queries;
use koan_core::player::commands::PlayerCommand;
use koan_core::player::state::{QueueItemId, SharedPlayerState};
use super::types::Conn;
pub(super) const DEFAULT_PAGE: usize = 50;
pub(super) const MAX_PAGE: usize = 500;
pub(super) fn page_size(first: Option<i32>) -> usize {
match first {
Some(f) => (f.max(0) as usize).min(MAX_PAGE),
None => DEFAULT_PAGE,
}
}
pub(super) fn page_offset(after: Option<&str>) -> usize {
after
.and_then(|c| c.parse::<usize>().ok())
.map(|i| i.saturating_add(1))
.unwrap_or(0)
}
pub(super) fn paginate<T: async_graphql::OutputType>(
items: Vec<T>,
after: Option<String>,
first: Option<i32>,
) -> async_graphql::Result<Conn<T>> {
let total = items.len();
let start = page_offset(after.as_deref()).min(total);
let end = start.saturating_add(page_size(first)).min(total);
let mut conn = Conn::new(start > 0, end < total);
for (i, item) in items.into_iter().enumerate().skip(start).take(end - start) {
conn.edges.push(Edge::new(i, item));
}
Ok(conn)
}
pub(super) fn paginate_window<T: async_graphql::OutputType>(
mut rows: Vec<T>,
offset: usize,
limit: usize,
) -> Conn<T> {
let has_next = rows.len() > limit;
rows.truncate(limit);
let mut conn = Conn::new(offset > 0, has_next);
for (i, item) in rows.into_iter().enumerate() {
conn.edges.push(Edge::new(offset + i, item));
}
conn
}
pub(super) fn extract_year(date: &str) -> Option<i32> {
date.get(..4).and_then(|s| s.parse().ok())
}
pub(super) fn album_year(album: &queries::AlbumRow) -> Option<i32> {
album.date.as_deref().and_then(extract_year)
}
pub(super) fn sync_favourite_to_remote(db: &Database, path: &str, star: bool) {
koan_core::helpers::sync_favourite_to_remote(db, std::path::Path::new(path), star);
}
pub(super) fn spawn_downloads(
pending: Vec<(i64, QueueItemId)>,
tx: Sender<PlayerCommand>,
state: Arc<SharedPlayerState>,
) {
koan_core::helpers::spawn_downloads(pending, tx, state);
}