pub(crate) fn parse_indices(
    input: &str,
    max_len: usize,
) -> std::result::Result<Vec<usize>, String> {
    let mut unique = Vec::new();

    for token in input.split(',') {
        let token = token.trim();
        if token.is_empty() {
            continue;
        }

        if let Some((start, end)) = token.split_once('-') {
            let start = start
                .trim()
                .parse::<usize>()
                .map_err(|_| "Invalid start of range".to_string())?;
            let end = end
                .trim()
                .parse::<usize>()
                .map_err(|_| "Invalid end of range".to_string())?;

            if start == 0 || end == 0 || start > end {
                return Err(
                    "Invalid range: indexes are 1-based and must be start <= end".to_string(),
                );
            }

            let (start, end) = (start - 1, end - 1);
            if end >= max_len {
                return Err(format!(
                    "Range end {input} is above result count {max_len}",
                    input = end + 1,
                ));
            }

            for index in start..=end {
                if unique.contains(&index) {
                    continue;
                }
                unique.push(index);
            }
            continue;
        }

        let index = token
            .parse::<usize>()
            .map_err(|_| format!("Invalid index '{token}'"))?;
        if index == 0 {
            return Err("Indexes are 1-based, use 1 or greater".to_string());
        }
        let index = index - 1;
        if index >= max_len {
            return Err(format!(
                "Index {} is above result count {}",
                index + 1,
                max_len
            ));
        }

        if !unique.contains(&index) {
            unique.push(index);
        }
    }

    if unique.is_empty() {
        return Err("No valid indexes provided".to_string());
    }

    Ok(unique)
}
