forked from lix-project/lix-installer
Allow users to answer e at prompts (#183)
* Allow users to answer e at prompts * Rename to currently_explaining * Speeling * Show options fully in prompt * Better coloring
This commit is contained in:
parent
4338b78135
commit
f1df7ed432
|
@ -42,7 +42,7 @@ impl Action for MoveUnpackedNix {
|
|||
vec![ActionDescription::new(
|
||||
format!("Move the downloaded Nix into `/nix`"),
|
||||
vec![format!(
|
||||
"Nix is being downloaded to `{}` and should be in `nix`",
|
||||
"Nix is being downloaded to `{}` and should be in `/nix`",
|
||||
self.src.display(),
|
||||
)],
|
||||
)]
|
||||
|
|
|
@ -61,8 +61,8 @@ impl Action for ConfigureNixDaemonService {
|
|||
self.tracing_synopsis(),
|
||||
vec![
|
||||
"Run `systemd-tempfiles --create --prefix=/nix/var/nix`".to_string(),
|
||||
"Run `systemctl link {SERVICE_SRC}`".to_string(),
|
||||
"Run `systemctl link {SOCKET_SRC}`".to_string(),
|
||||
format!("Run `systemctl link {SERVICE_SRC}`"),
|
||||
format!("Run `systemctl link {SOCKET_SRC}`"),
|
||||
"Run `systemctl daemon-reload`".to_string(),
|
||||
],
|
||||
)]
|
||||
|
|
|
@ -52,21 +52,17 @@ impl Action for CreateNixTree {
|
|||
}
|
||||
|
||||
fn execute_description(&self) -> Vec<ActionDescription> {
|
||||
let Self { create_directories } = &self;
|
||||
|
||||
let mut create_directory_descriptions = Vec::new();
|
||||
for create_directory in create_directories {
|
||||
if let Some(val) = create_directory.describe_execute().iter().next() {
|
||||
create_directory_descriptions.push(val.description.clone())
|
||||
}
|
||||
}
|
||||
vec![ActionDescription::new(
|
||||
self.tracing_synopsis(),
|
||||
vec![
|
||||
format!(
|
||||
"Nix and the Nix daemon require a Nix Store, which will be stored at `/nix`"
|
||||
),
|
||||
format!(
|
||||
"Creates: {}",
|
||||
PATHS
|
||||
.iter()
|
||||
.map(|v| format!("`{v}`"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
),
|
||||
],
|
||||
create_directory_descriptions,
|
||||
)]
|
||||
}
|
||||
|
||||
|
|
|
@ -3,11 +3,22 @@ use std::io::{stdin, stdout, BufRead, Write};
|
|||
use eyre::{eyre, WrapErr};
|
||||
use owo_colors::OwoColorize;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum PromptChoice {
|
||||
Yes,
|
||||
No,
|
||||
Explain,
|
||||
}
|
||||
|
||||
// Do not try to get clever!
|
||||
//
|
||||
// Mac is extremely janky if you `curl $URL | sudo sh` and the TTY may not be set up right.
|
||||
// The below method was adopted from Rustup at https://github.com/rust-lang/rustup/blob/3331f34c01474bf216c99a1b1706725708833de1/src/cli/term2.rs#L37
|
||||
pub(crate) async fn confirm(question: impl AsRef<str>, default: bool) -> eyre::Result<bool> {
|
||||
pub(crate) async fn prompt(
|
||||
question: impl AsRef<str>,
|
||||
default: PromptChoice,
|
||||
currently_explaining: bool,
|
||||
) -> eyre::Result<PromptChoice> {
|
||||
let stdout = stdout();
|
||||
let mut term =
|
||||
term::terminfo::TerminfoTerminal::new(stdout).ok_or(eyre!("Couldn't get terminal"))?;
|
||||
|
@ -15,12 +26,34 @@ pub(crate) async fn confirm(question: impl AsRef<str>, default: bool) -> eyre::R
|
|||
"\
|
||||
{question}\n\
|
||||
\n\
|
||||
{are_you_sure} ({yes}/{no}): \
|
||||
{are_you_sure} ({yes}/{no}{maybe_explain}): \
|
||||
",
|
||||
question = question.as_ref(),
|
||||
are_you_sure = "Proceed?".bold(),
|
||||
no = if default { "n" } else { "N" }.red(),
|
||||
yes = if default { "Y" } else { "y" }.green(),
|
||||
no = if default == PromptChoice::No {
|
||||
"[N]o"
|
||||
} else {
|
||||
"[n]o"
|
||||
}
|
||||
.red(),
|
||||
yes = if default == PromptChoice::Yes {
|
||||
"[Y]es"
|
||||
} else {
|
||||
"[y]es"
|
||||
}
|
||||
.green(),
|
||||
maybe_explain = if !currently_explaining {
|
||||
format!(
|
||||
"/{}",
|
||||
if default == PromptChoice::Explain {
|
||||
"[E]xplain"
|
||||
} else {
|
||||
"[e]xplain"
|
||||
}
|
||||
)
|
||||
} else {
|
||||
"".into()
|
||||
},
|
||||
);
|
||||
|
||||
term.write_all(with_confirm.as_bytes())?;
|
||||
|
@ -29,10 +62,11 @@ pub(crate) async fn confirm(question: impl AsRef<str>, default: bool) -> eyre::R
|
|||
let input = read_line()?;
|
||||
|
||||
let r = match &*input.to_lowercase() {
|
||||
"y" | "yes" => true,
|
||||
"n" | "no" => false,
|
||||
"y" | "yes" => PromptChoice::Yes,
|
||||
"n" | "no" => PromptChoice::No,
|
||||
"e" | "explain" => PromptChoice::Explain,
|
||||
"" => default,
|
||||
_ => false,
|
||||
_ => PromptChoice::No,
|
||||
};
|
||||
|
||||
Ok(r)
|
||||
|
|
|
@ -6,7 +6,11 @@ use std::{
|
|||
|
||||
use crate::{
|
||||
action::ActionState,
|
||||
cli::{ensure_root, interaction, signal_channel, CommandExecute},
|
||||
cli::{
|
||||
ensure_root,
|
||||
interaction::{self, PromptChoice},
|
||||
signal_channel, CommandExecute,
|
||||
},
|
||||
error::HasExpectedErrors,
|
||||
plan::RECEIPT_LOCATION,
|
||||
planner::Planner,
|
||||
|
@ -144,15 +148,23 @@ impl CommandExecute for Install {
|
|||
};
|
||||
|
||||
if !no_confirm {
|
||||
if !interaction::confirm(
|
||||
install_plan
|
||||
.describe_install(explain)
|
||||
.map_err(|e| eyre!(e))?,
|
||||
true,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
interaction::clean_exit_with_message("Okay, didn't do anything! Bye!").await;
|
||||
let mut currently_explaining = explain;
|
||||
loop {
|
||||
match interaction::prompt(
|
||||
install_plan
|
||||
.describe_install(currently_explaining)
|
||||
.map_err(|e| eyre!(e))?,
|
||||
PromptChoice::Yes,
|
||||
currently_explaining,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
PromptChoice::Yes => break,
|
||||
PromptChoice::Explain => currently_explaining = true,
|
||||
PromptChoice::No => {
|
||||
interaction::clean_exit_with_message("Okay, didn't do anything! Bye!").await
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,16 +187,26 @@ impl CommandExecute for Install {
|
|||
};
|
||||
|
||||
eprintln!("{}", "Installation failure, offering to revert...".red());
|
||||
if !interaction::confirm(
|
||||
install_plan
|
||||
.describe_uninstall(explain)
|
||||
.map_err(|e| eyre!(e))?,
|
||||
true,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
interaction::clean_exit_with_message("Okay, didn't do anything! Bye!")
|
||||
.await;
|
||||
let mut currently_explaining = explain;
|
||||
loop {
|
||||
match interaction::prompt(
|
||||
install_plan
|
||||
.describe_uninstall(currently_explaining)
|
||||
.map_err(|e| eyre!(e))?,
|
||||
PromptChoice::Yes,
|
||||
currently_explaining,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
PromptChoice::Yes => break,
|
||||
PromptChoice::Explain => currently_explaining = true,
|
||||
PromptChoice::No => {
|
||||
interaction::clean_exit_with_message(
|
||||
"Okay, didn't do anything! Bye!",
|
||||
)
|
||||
.await
|
||||
},
|
||||
}
|
||||
}
|
||||
let rx2 = tx.subscribe();
|
||||
let res = install_plan.uninstall(rx2).await;
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
cli::{ensure_root, signal_channel},
|
||||
cli::{ensure_root, interaction::PromptChoice, signal_channel},
|
||||
error::HasExpectedErrors,
|
||||
plan::RECEIPT_LOCATION,
|
||||
InstallPlan,
|
||||
|
@ -127,13 +127,22 @@ impl CommandExecute for Uninstall {
|
|||
let mut plan: InstallPlan = serde_json::from_str(&install_receipt_string)?;
|
||||
|
||||
if !no_confirm {
|
||||
if !interaction::confirm(
|
||||
plan.describe_uninstall(explain).map_err(|e| eyre!(e))?,
|
||||
true,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
interaction::clean_exit_with_message("Okay, didn't do anything! Bye!").await;
|
||||
let mut currently_explaining = explain;
|
||||
loop {
|
||||
match interaction::prompt(
|
||||
plan.describe_uninstall(currently_explaining)
|
||||
.map_err(|e| eyre!(e))?,
|
||||
PromptChoice::Yes,
|
||||
currently_explaining,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
PromptChoice::Yes => break,
|
||||
PromptChoice::Explain => currently_explaining = true,
|
||||
PromptChoice::No => {
|
||||
interaction::clean_exit_with_message("Okay, didn't do anything! Bye!").await
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -66,15 +66,10 @@ impl InstallPlan {
|
|||
\n\
|
||||
{plan_settings}\n\
|
||||
\n\
|
||||
The following actions will be taken{maybe_explain}:\n\
|
||||
The following actions will be taken:\n\
|
||||
\n\
|
||||
{actions}\n\
|
||||
",
|
||||
maybe_explain = if !explain {
|
||||
" (`--explain` for more context)"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
planner = planner.typetag_name(),
|
||||
plan_settings = planner
|
||||
.settings()?
|
||||
|
|
Loading…
Reference in a new issue