oxyromon 0.11.1

Rusty ROM OrgaNizer
use super::database::*;
use super::model::*;
use dialoguer::{Confirm, MultiSelect, Select};
use simple_error::SimpleResult;
use sqlx::sqlite::SqliteConnection;

pub async fn prompt_for_systems(
    connection: &mut SqliteConnection,
    url: Option<&str>,
    arcade_only: bool,
    all: bool,
) -> SimpleResult<Vec<System>> {
    let systems = if arcade_only {
        find_arcade_systems(connection).await
    } else {
        match url {
            Some(url) => find_systems_by_url(connection, url).await,
            None => find_systems(connection).await,
        }
    };

    if all || systems.is_empty() {
        return Ok(systems);
    }

    let indices = multiselect(
        &systems
            .iter()
            .map(|system| &system.name)
            .collect::<Vec<&String>>(),
        "Please select systems",
        None,
        None,
    )?;
    Ok(systems
        .into_iter()
        .enumerate()
        .filter(|(i, _)| indices.contains(i))
        .map(|(_, system)| system)
        .collect())
}

pub async fn prompt_for_system(
    connection: &mut SqliteConnection,
    default: Option<usize>,
) -> SimpleResult<System> {
    let mut systems = find_systems(connection).await;
    match systems.len() {
        0 => bail!("No available system"),
        1 => Ok(systems.remove(0)),
        _ => {
            let index = select(
                &systems
                    .iter()
                    .map(|system| &system.name)
                    .collect::<Vec<&String>>(),
                "Please select a system",
                default,
                None,
            )?;
            Ok(systems.remove(index))
        }
    }
}

#[cfg(feature = "ird")]
pub async fn prompt_for_system_like(
    connection: &mut SqliteConnection,
    default: Option<usize>,
    name: &str,
) -> SimpleResult<System> {
    let mut systems = find_systems_by_name_like(connection, name).await;
    match systems.len() {
        0 => bail!("No available system"),
        1 => Ok(systems.remove(0)),
        _ => {
            let index = select(
                &systems
                    .iter()
                    .map(|system| &system.name)
                    .collect::<Vec<&String>>(),
                "Please select a system",
                default,
                None,
            )?;
            Ok(systems.remove(index))
        }
    }
}

pub fn prompt_for_games(games: Vec<Game>, all: bool) -> SimpleResult<Vec<Game>> {
    if all || games.is_empty() {
        return Ok(games);
    }

    let indices = multiselect(
        &games
            .iter()
            .map(|game| &game.name)
            .collect::<Vec<&String>>(),
        "Please select games",
        None,
        Some(10),
    )?;
    Ok(games
        .into_iter()
        .enumerate()
        .filter(|(i, _)| indices.contains(i))
        .map(|(_, game)| game)
        .collect())
}

#[cfg(feature = "ird")]
pub fn prompt_for_game(games: &[Game]) -> SimpleResult<Option<&Game>> {
    let index = select_opt(
        &games
            .iter()
            .map(|game| &game.name)
            .collect::<Vec<&String>>(),
        "Please select a game",
        None,
        Some(10),
    )?;
    Ok(index.map(|i| games.get(i).unwrap()))
}

#[cfg(feature = "ird")]
pub fn prompt_for_rom(roms: &mut Vec<Rom>, default: Option<usize>) -> SimpleResult<Option<Rom>> {
    match roms.len() {
        0 => bail!("No available rom"),
        1 => Ok(Some(roms.remove(0))),
        _ => {
            let index = select_opt(
                &roms.iter().map(|rom| &rom.name).collect::<Vec<&String>>(),
                "Please select a ROM",
                default,
                Some(10),
            )?;
            Ok(index.map(|i| roms.remove(i)))
        }
    }
}

pub fn prompt_for_rom_game(roms_games: &mut Vec<(Rom, Game)>) -> SimpleResult<Option<Rom>> {
    let index = select_opt(
        &roms_games
            .iter()
            .map(|(rom, game)| format!("{} ({})", &rom.name, &game.name))
            .collect::<Vec<String>>(),
        "Please select a ROM",
        None,
        Some(10),
    )?;
    Ok(index.map(|i| roms_games.remove(i).0))
}

pub fn confirm(default: bool) -> SimpleResult<bool> {
    Ok(try_with!(
        Confirm::new()
            .with_prompt("Proceed?")
            .default(default)
            .interact(),
        "Failed to get user input"
    ))
}

pub fn select<T: ToString>(
    items: &[T],
    prompt: &str,
    default: Option<usize>,
    max_length: Option<usize>,
) -> SimpleResult<usize> {
    let mut select = Select::new();
    select.items(items).with_prompt(prompt);
    if let Some(default) = default {
        select.default(default);
    }
    if let Some(max_length) = max_length {
        select.max_length(max_length);
    }
    Ok(try_with!(select.interact(), "Failed to get user input"))
}

pub fn select_opt<T: ToString>(
    items: &[T],
    prompt: &str,
    default: Option<usize>,
    max_length: Option<usize>,
) -> SimpleResult<Option<usize>> {
    let mut select = Select::new();
    select.items(items).with_prompt(prompt);
    if let Some(default) = default {
        select.default(default);
    }
    if let Some(max_length) = max_length {
        select.max_length(max_length);
    }
    Ok(try_with!(select.interact_opt(), "Failed to get user input"))
}

pub fn multiselect<T: ToString>(
    items: &[T],
    prompt: &str,
    defaults: Option<&[bool]>,
    max_length: Option<usize>,
) -> SimpleResult<Vec<usize>> {
    let mut multiselect = MultiSelect::new();
    multiselect.items(items).with_prompt(prompt);
    if let Some(defaults) = defaults {
        multiselect.defaults(defaults);
    }
    if let Some(max_length) = max_length {
        multiselect.max_length(max_length);
    }
    Ok(try_with!(
        multiselect.interact(),
        "Failed to get user input"
    ))
}