1use anyhow::{anyhow, Result};
4
5use crate::client::RommClient;
6use crate::endpoints::collections::{ListCollections, ListSmartCollections};
7use crate::endpoints::platforms::ListPlatforms;
8use crate::types::{Collection, Platform};
9
10pub fn resolve_platform_id_from_list(query: &str, platforms: &[Platform]) -> Result<u64> {
12 let normalized = query.trim().to_ascii_lowercase();
13
14 if let Some(platform) = platforms.iter().find(|p| {
15 p.slug.eq_ignore_ascii_case(&normalized) || p.fs_slug.eq_ignore_ascii_case(&normalized)
16 }) {
17 return Ok(platform.id);
18 }
19
20 let exact_name_matches: Vec<&Platform> = platforms
21 .iter()
22 .filter(|p| {
23 p.name.eq_ignore_ascii_case(&normalized)
24 || p.display_name
25 .as_deref()
26 .is_some_and(|name| name.eq_ignore_ascii_case(&normalized))
27 || p.custom_name
28 .as_deref()
29 .is_some_and(|name| name.eq_ignore_ascii_case(&normalized))
30 })
31 .collect();
32
33 match exact_name_matches.len() {
34 1 => Ok(exact_name_matches[0].id),
35 0 => Err(anyhow!(
36 "No platform found for '{}'. Use 'romm-cli platforms list' to inspect available values.",
37 query
38 )),
39 _ => {
40 let names = exact_name_matches
41 .iter()
42 .map(|p| format!("{} ({})", p.name, p.id))
43 .collect::<Vec<_>>()
44 .join(", ");
45 Err(anyhow!(
46 "Platform '{}' is ambiguous. Matches: {}. Please use a more specific --platform value.",
47 query,
48 names
49 ))
50 }
51 }
52}
53
54pub async fn resolve_platform_id(
56 client: &RommClient,
57 platform_query: Option<&str>,
58) -> Result<Option<u64>> {
59 let Some(query) = platform_query.map(str::trim).filter(|q| !q.is_empty()) else {
60 return Ok(None);
61 };
62 let platforms = client.call(&ListPlatforms).await?;
63 resolve_platform_id_from_list(query, &platforms).map(Some)
64}
65
66pub async fn resolve_platform_ids(client: &RommClient, names: &[String]) -> Result<Vec<u64>> {
68 if names.is_empty() {
69 return Ok(Vec::new());
70 }
71 let platforms = client.call(&ListPlatforms).await?;
72 let mut out = Vec::new();
73 for n in names {
74 let id = resolve_platform_id_from_list(n.trim(), &platforms)?;
75 if !out.contains(&id) {
76 out.push(id);
77 }
78 }
79 Ok(out)
80}
81
82fn match_collections_by_name<'a>(q: &str, collections: &'a [Collection]) -> Vec<&'a Collection> {
83 let n = q.trim().to_ascii_lowercase();
84 collections
85 .iter()
86 .filter(|c| c.name.eq_ignore_ascii_case(&n))
87 .collect()
88}
89
90pub async fn resolve_manual_collection_id(
92 client: &RommClient,
93 query: Option<&str>,
94) -> Result<Option<u64>> {
95 let Some(q) = query.map(str::trim).filter(|s| !s.is_empty()) else {
96 return Ok(None);
97 };
98 if let Ok(id) = q.parse::<u64>() {
99 return Ok(Some(id));
100 }
101 let list = client.call(&ListCollections).await?.into_vec();
102 let matches = match_collections_by_name(q, &list);
103 match matches.len() {
104 0 => Err(anyhow!(
105 "No manual collection named '{}'. Use `romm-cli collections list`.",
106 q
107 )),
108 1 => Ok(Some(matches[0].id)),
109 _ => Err(anyhow!(
110 "Manual collection '{}' is ambiguous among {} matches; use a numeric id.",
111 q,
112 matches.len()
113 )),
114 }
115}
116
117pub async fn resolve_smart_collection_id(
119 client: &RommClient,
120 query: Option<&str>,
121) -> Result<Option<u64>> {
122 let Some(q) = query.map(str::trim).filter(|s| !s.is_empty()) else {
123 return Ok(None);
124 };
125 if let Ok(id) = q.parse::<u64>() {
126 return Ok(Some(id));
127 }
128 let list = client.call(&ListSmartCollections).await?.into_vec();
129 let matches = match_collections_by_name(q, &list);
130 match matches.len() {
131 0 => Err(anyhow!(
132 "No smart collection named '{}'. Use `romm-cli collections list`.",
133 q
134 )),
135 1 => Ok(Some(matches[0].id)),
136 _ => Err(anyhow!(
137 "Smart collection '{}' is ambiguous among {} matches; use a numeric id.",
138 q,
139 matches.len()
140 )),
141 }
142}