Skip to main content

romm_api/core/
roms.rs

1//! Shared ROM list fetching helpers.
2
3use anyhow::Result;
4
5use crate::client::RommClient;
6use crate::endpoints::roms::GetRoms;
7use crate::types::RomList;
8
9/// Maximum ROM rows fetched by paginated helpers (TUI safety cap).
10pub const ROM_PAGE_CEILING: u64 = 20000;
11
12/// True when a paginated ROM list has enough rows to stop fetching
13/// (`items.len() >= total`, or the safety ceiling was hit).
14pub fn rom_list_fetch_complete(list: &RomList) -> bool {
15    let loaded = list.items.len() as u64;
16    loaded >= list.total || loaded >= ROM_PAGE_CEILING
17}
18
19/// Fetch all pages for a `GetRoms` request up to [`ROM_PAGE_CEILING`].
20pub async fn fetch_roms_paginated(client: &RommClient, req: &GetRoms) -> Result<RomList> {
21    let mut roms = client.call(req).await?;
22    while !rom_list_fetch_complete(&roms) {
23        let mut next_req = req.clone();
24        next_req.offset = Some(roms.items.len() as u32);
25        let next_batch = client.call(&next_req).await?;
26        if next_batch.items.is_empty() {
27            break;
28        }
29        roms.items.extend(next_batch.items);
30    }
31    Ok(roms)
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37    use crate::types::Rom;
38
39    fn minimal_rom(id: u64) -> Rom {
40        Rom {
41            id,
42            platform_id: 10,
43            platform_slug: None,
44            platform_fs_slug: None,
45            platform_custom_name: None,
46            platform_display_name: None,
47            fs_name: format!("game{id}.zip"),
48            fs_name_no_tags: format!("game{id}"),
49            fs_name_no_ext: format!("game{id}"),
50            fs_extension: "zip".to_string(),
51            fs_path: format!("/roms/game{id}.zip"),
52            fs_size_bytes: 1,
53            name: format!("Game {id}"),
54            slug: None,
55            summary: None,
56            path_cover_small: None,
57            path_cover_large: None,
58            url_cover: None,
59            has_manual: false,
60            path_manual: None,
61            url_manual: None,
62            is_unidentified: false,
63            is_identified: true,
64            files: Vec::new(),
65            ra_id: None,
66            merged_ra_metadata: None,
67        }
68    }
69
70    fn list(items: usize, total: u64) -> RomList {
71        RomList {
72            items: (0..items).map(|i| minimal_rom(i as u64)).collect(),
73            total,
74            limit: 50,
75            offset: 0,
76        }
77    }
78
79    #[test]
80    fn complete_when_items_cover_total() {
81        assert!(rom_list_fetch_complete(&list(3, 3)));
82    }
83
84    #[test]
85    fn incomplete_when_short_of_total() {
86        assert!(!rom_list_fetch_complete(&list(1, 100)));
87    }
88}