use crate::config::Config;
use crate::content::RecipeItem;
use crate::menus::multiselect_truncate_formatter;
use crate::recipe::Recipe;
use super::file_editor::FileEditor;
use super::{ContentKind, EditorAction, prompt_new_name};
use std::collections::HashSet;
use std::path::PathBuf;
use inquire::error::InquireResult;
use inquire::formatter::MultiOptionFormatter;
use inquire::validator::{Validation, ValueRequiredValidator};
use inquire::{MultiSelect, Select, Text};
use rust_i18n::t;
use strum::IntoEnumIterator;
macro_rules! try_prompt {
($expr:expr) => {
match $expr? {
Some(val) => val,
None => return Ok(false),
}
};
}
#[macro_export]
macro_rules! set_if_changed {
($l_val:expr, $r_val:expr) => {
if $l_val != $r_val {
$l_val = $r_val;
true
} else {
false
}
};
}
impl EditorAction {
pub fn edit_name(&self, recipe: &mut Recipe) -> InquireResult<bool> {
let name = try_prompt!(
Text::new(&t!("menus.imprint.get_name"))
.with_validator(ValueRequiredValidator::new(t!(
"menus.imprint.name_required"
)))
.with_initial_value(&recipe.name)
.prompt_skippable()
);
Ok(set_if_changed!(recipe.name, name))
}
pub fn edit_description(&self, recipe: &mut Recipe) -> InquireResult<bool> {
let desc = try_prompt!(
Text::new(&t!("menus.imprint.get_desc"))
.with_initial_value(&recipe.description)
.prompt_skippable()
);
Ok(set_if_changed!(recipe.description, desc))
}
pub fn add_content(&self, recipe: &mut Recipe) -> InquireResult<bool> {
let name = try_prompt!(
Text::new(&t!("menus.editor.get_content_name"))
.with_validator(ValueRequiredValidator::new(t!(
"menus.editor.content_name_required"
)))
.with_validator(|i: &str| {
if recipe.contents.iter().any(|item| item.name() == i) {
Ok(Validation::Invalid(
t!("menus.editor.invalid_content_name").into(),
))
} else {
Ok(Validation::Valid)
}
})
.prompt_skippable()
);
let path = PathBuf::from(name);
let content_kind =
try_prompt!(Select::new(&t!(""), ContentKind::iter().collect()).prompt_skippable());
match content_kind {
ContentKind::File => {
let extension = path
.extension()
.unwrap_or_default()
.to_str()
.unwrap_or_default();
let contents = FileEditor::new("", extension).run()?;
recipe.contents.push(RecipeItem::File(crate::content::File {
name: path,
content: contents,
}));
Ok(true)
}
ContentKind::Directory => {
recipe.contents.push(RecipeItem::Directory(path));
Ok(true)
}
}
}
pub fn edit_content(&self, recipe: &mut Recipe) -> InquireResult<bool> {
let use_vim_mode: bool = Config::get().unwrap().vim;
let content_item = try_prompt!(
Select::new(&t!("menus.editor.select_content"), recipe.contents.clone())
.with_help_message(&t!("menus.select_help"))
.with_vim_mode(use_vim_mode)
.prompt_skippable()
);
match content_item {
RecipeItem::File(ref f) => {
let name = try_prompt!(prompt_new_name(
f.name.to_str().unwrap_or_default(),
&recipe.contents
));
let path = PathBuf::from(name);
let extension = path
.extension()
.unwrap_or_default()
.to_str()
.unwrap_or_default();
let content = FileEditor::new(&f.content, extension).run()?;
Ok(recipe.replace_content(
&f.name,
RecipeItem::File(crate::content::File {
name: path,
content,
}),
))
}
RecipeItem::Directory(ref p) => {
let name = try_prompt!(prompt_new_name(
p.to_str().unwrap_or_default(),
&recipe.contents
));
Ok(recipe.replace_content(p, RecipeItem::Directory(PathBuf::from(name))))
}
}
}
pub fn remove_contents(&self, recipe: &mut Recipe) -> InquireResult<bool> {
let formatter: MultiOptionFormatter<RecipeItem> = &multiselect_truncate_formatter;
let use_vim_mode = Config::get().unwrap().vim;
let contents = try_prompt!(
MultiSelect::new(&t!("menus.imprint.filter_rec"), recipe.contents.clone())
.with_formatter(formatter)
.with_help_message(&t!("menus.multiselect_help"))
.with_vim_mode(use_vim_mode)
.prompt_skippable()
);
if contents.is_empty() {
return Ok(false);
}
let paths_to_remove: HashSet<_> = contents.into_iter().collect();
recipe.contents.retain(|r| !paths_to_remove.contains(r));
Ok(true)
}
}