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