Framework for embedding localizations into Rust types
//! Example showing various localized interactions

use icu_locale::locale;
use l10n_embed_derive::localize;
use l10n_embed_interaction::{Confirm, Editor, Input, Password, Select};
use l10n_embed_interaction::{InteractionEnvironment, InteractionError};

#[localize("examples/prompt/locale/**/prompt_selection.ftl")]
enum PromptSelection {
    Confirm,
    Editor,
    Input,
    Password,
}

#[localize("examples/prompt/locale/**/prompts.ftl")]
enum Prompt {
    Confirm,
    Input,
    Password,
    PasswordConfirmation,
    Select,
}

#[localize("examples/prompt/locale/**/errors.ftl")]
enum PromptError {
    NonAlphabeticInput,
    NonPalindromeInput,
    PasswordTooShort {
        provided_length: usize,
        expected_length: usize,
    },
    PasswordMismatch,
}

const ALWAYS_INTERACTIVE: &str = "Attempted to interact in a non-interactive environment";
const DEFAULT_SELECTION: usize = 2;
const MINIMUM_PASSWORD_LENGTH: usize = 2;
const PROMPT_SELECTIONS: [PromptSelection; 4] = [
    PromptSelection::Confirm,
    PromptSelection::Editor,
    PromptSelection::Input,
    PromptSelection::Password,
];

fn main() -> Result<(), InteractionError> {
    let environment = InteractionEnvironment::new(locale!("en-US"), true);

    let selected_index = Select::new(&environment, Prompt::Select)
        .with_items(&PROMPT_SELECTIONS)
        .with_default(DEFAULT_SELECTION)
        .interact()?
        .expect(ALWAYS_INTERACTIVE);

    match PROMPT_SELECTIONS[selected_index] {
        PromptSelection::Confirm => {
            let confirmation = Confirm::new(&environment, Prompt::Confirm)
                .with_default(true)
                .interact()?
                .expect(ALWAYS_INTERACTIVE);

            let message = match confirmation {
                true => "hooray!",
                false => "dang..",
            };
            println!("{message}");
        }
        PromptSelection::Editor => {
            let edited = Editor::new(&environment, "toml").edit("Some text for you to edit!")?;

            match edited {
                Some(saved_text) => println!(
                    "The file was saved successfully (total lines: {})",
                    saved_text.lines().count()
                ),
                None => println!("No text was saved.."),
            }
        }
        PromptSelection::Input => {
            let input = Input::new(&environment, Prompt::Input)
                // `Input::with_default` accepts anything that implements `Localize`,
                // so this default could also be localized
                .with_default("racecar")
                .with_validator(|input| {
                    if !input
                        .chars()
                        .all(|character| character.is_alphabetic() && !character.is_whitespace())
                    {
                        Err(PromptError::NonAlphabeticInput)
                    } else if input != &input.chars().rev().collect::<String>() {
                        Err(PromptError::NonPalindromeInput)
                    } else {
                        Ok(())
                    }
                })
                .interact()?
                .expect(ALWAYS_INTERACTIVE);

            println!("Your palindrome is: {input}!")
        }
        PromptSelection::Password => {
            let password = Password::new(&environment, Prompt::Password)
                .with_validator(|password| {
                    if password.len() >= MINIMUM_PASSWORD_LENGTH {
                        Ok(())
                    } else {
                        Err(PromptError::PasswordTooShort {
                            provided_length: password.len(),
                            expected_length: MINIMUM_PASSWORD_LENGTH,
                        })
                    }
                })
                .with_confirmation(Prompt::PasswordConfirmation, PromptError::PasswordMismatch)
                .interact()?
                .expect(ALWAYS_INTERACTIVE);

            println!("Your password is: {password}");
        }
    }

    Ok(())
}