Improve planner and plan output

This commit is contained in:
Ana Hobden 2022-10-28 12:44:07 -07:00
parent 615b8b502c
commit eb1d8215f3
35 changed files with 181 additions and 45 deletions

View file

@ -65,7 +65,7 @@ impl ConfigureNix {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "configure-nix")] #[typetag::serde(name = "configure_nix")]
impl Action for ConfigureNix { impl Action for ConfigureNix {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
let Self { let Self {

View file

@ -45,7 +45,7 @@ impl ConfigureNixDaemonService {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "configure-nix-daemon")] #[typetag::serde(name = "configure_nix_daemon")]
impl Action for ConfigureNixDaemonService { impl Action for ConfigureNixDaemonService {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed { if self.action_state == ActionState::Completed {

View file

@ -58,7 +58,7 @@ impl ConfigureShellProfile {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "configure-shell-profile")] #[typetag::serde(name = "configure_shell_profile")]
impl Action for ConfigureShellProfile { impl Action for ConfigureShellProfile {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed { if self.action_state == ActionState::Completed {

View file

@ -67,7 +67,7 @@ impl CreateDirectory {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "create-directory")] #[typetag::serde(name = "create_directory")]
impl Action for CreateDirectory { impl Action for CreateDirectory {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
let Self { let Self {

View file

@ -51,7 +51,7 @@ impl CreateFile {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "create-file")] #[typetag::serde(name = "create_file")]
impl Action for CreateFile { impl Action for CreateFile {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
let Self { let Self {

View file

@ -26,7 +26,7 @@ impl CreateGroup {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "create-group")] #[typetag::serde(name = "create_group")]
impl Action for CreateGroup { impl Action for CreateGroup {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
let Self { let Self {

View file

@ -40,7 +40,7 @@ impl CreateNixTree {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "creat-nix-tree")] #[typetag::serde(name = "create_nix_tree")]
impl Action for CreateNixTree { impl Action for CreateNixTree {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed { if self.action_state == ActionState::Completed {

View file

@ -48,7 +48,7 @@ impl CreateOrAppendFile {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "create-or-append-file")] #[typetag::serde(name = "create_or_append_file")]
impl Action for CreateOrAppendFile { impl Action for CreateOrAppendFile {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
let Self { let Self {

View file

@ -30,7 +30,7 @@ impl CreateUser {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "create-user")] #[typetag::serde(name = "create_user")]
impl Action for CreateUser { impl Action for CreateUser {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed { if self.action_state == ActionState::Completed {

View file

@ -55,7 +55,7 @@ impl CreateUsersAndGroup {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "create-users-and-group")] #[typetag::serde(name = "create_users_and_group")]
impl Action for CreateUsersAndGroup { impl Action for CreateUsersAndGroup {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
let Self { let Self {

View file

@ -32,7 +32,7 @@ impl FetchNix {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "fetch-nix")] #[typetag::serde(name = "fetch_nix")]
impl Action for FetchNix { impl Action for FetchNix {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
let Self { let Self {

View file

@ -25,7 +25,7 @@ impl MoveUnpackedNix {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "mount-unpacked-nix")] #[typetag::serde(name = "mount_unpacked_nix")]
impl Action for MoveUnpackedNix { impl Action for MoveUnpackedNix {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed { if self.action_state == ActionState::Completed {

View file

@ -45,7 +45,7 @@ impl PlaceChannelConfiguration {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "place-channel-configuration")] #[typetag::serde(name = "place_channel_configuration")]
impl Action for PlaceChannelConfiguration { impl Action for PlaceChannelConfiguration {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
let Self { let Self {

View file

@ -42,7 +42,7 @@ impl PlaceNixConfiguration {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "place-nix-configuration")] #[typetag::serde(name = "place_nix_configuration")]
impl Action for PlaceNixConfiguration { impl Action for PlaceNixConfiguration {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed { if self.action_state == ActionState::Completed {

View file

@ -52,7 +52,7 @@ impl ProvisionNix {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "provision-nix")] #[typetag::serde(name = "provision_nix")]
impl Action for ProvisionNix { impl Action for ProvisionNix {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
let Self { let Self {

View file

@ -23,7 +23,7 @@ impl SetupDefaultProfile {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "setup-default-profile")] #[typetag::serde(name = "setup_default_profile")]
impl Action for SetupDefaultProfile { impl Action for SetupDefaultProfile {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed { if self.action_state == ActionState::Completed {

View file

@ -28,7 +28,7 @@ impl BootstrapVolume {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "bootstrap-volume")] #[typetag::serde(name = "bootstrap_volume")]
impl Action for BootstrapVolume { impl Action for BootstrapVolume {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed { if self.action_state == ActionState::Completed {

View file

@ -145,7 +145,7 @@ impl CreateApfsVolume {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "create-apfs-volume")] #[typetag::serde(name = "create_apfs_volume")]
impl Action for CreateApfsVolume { impl Action for CreateApfsVolume {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
let Self { let Self {

View file

@ -19,7 +19,7 @@ impl CreateSyntheticObjects {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "create-synthetic-objects")] #[typetag::serde(name = "create_synthetic_objects")]
impl Action for CreateSyntheticObjects { impl Action for CreateSyntheticObjects {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed { if self.action_state == ActionState::Completed {

View file

@ -34,7 +34,7 @@ impl CreateVolume {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "create-volume")] #[typetag::serde(name = "create_volume")]
impl Action for CreateVolume { impl Action for CreateVolume {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed { if self.action_state == ActionState::Completed {

View file

@ -30,7 +30,7 @@ impl EnableOwnership {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "enable-ownership")] #[typetag::serde(name = "enable_ownership")]
impl Action for EnableOwnership { impl Action for EnableOwnership {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed { if self.action_state == ActionState::Completed {

View file

@ -24,7 +24,7 @@ impl EncryptVolume {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "encrypt-volume")] #[typetag::serde(name = "encrypt_volume")]
impl Action for EncryptVolume { impl Action for EncryptVolume {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed { if self.action_state == ActionState::Completed {

View file

@ -24,7 +24,7 @@ impl KickstartLaunchctlService {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "kickstart-launchctl-service")] #[typetag::serde(name = "kickstart_launchctl_service")]
impl Action for KickstartLaunchctlService { impl Action for KickstartLaunchctlService {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
let Self { unit, action_state } = self; let Self { unit, action_state } = self;

View file

@ -32,7 +32,7 @@ impl UnmountVolume {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "unmount-volume")] #[typetag::serde(name = "unmount_volume")]
impl Action for UnmountVolume { impl Action for UnmountVolume {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed { if self.action_state == ActionState::Completed {

View file

@ -92,7 +92,7 @@ impl CreateSystemdSysext {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "create-systemd-sysext")] #[typetag::serde(name = "create_systemd_sysext")]
impl Action for CreateSystemdSysext { impl Action for CreateSystemdSysext {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
let Self { let Self {

View file

@ -24,7 +24,7 @@ impl StartSystemdUnit {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "start-systemd-unit")] #[typetag::serde(name = "start_systemd_unit")]
impl Action for StartSystemdUnit { impl Action for StartSystemdUnit {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed { if self.action_state == ActionState::Completed {

View file

@ -26,7 +26,7 @@ impl SystemdSysextMerge {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "systemd-sysext-merge")] #[typetag::serde(name = "systemd_sysext_merge")]
impl Action for SystemdSysextMerge { impl Action for SystemdSysextMerge {
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
let Self { let Self {

View file

@ -54,7 +54,7 @@ impl CommandExecute for Install {
}; };
if !no_confirm { if !no_confirm {
if !interaction::confirm(plan.describe_execute(explain)).await? { if !interaction::confirm(plan.describe_execute(explain).map_err(|e| eyre!(e))?).await? {
interaction::clean_exit_with_message("Okay, didn't do anything! Bye!").await; interaction::clean_exit_with_message("Okay, didn't do anything! Bye!").await;
} }
} }

View file

@ -13,7 +13,7 @@ pub(crate) async fn confirm(question: impl AsRef<str>) -> eyre::Result<bool> {
{are_you_sure} ({yes}/{no}): \ {are_you_sure} ({yes}/{no}): \
", ",
question = question.as_ref(), question = question.as_ref(),
are_you_sure = "Are you sure?".bright_white().bold(), are_you_sure = "Proceed?".bright_white().bold(),
no = "N".red().bold(), no = "N".red().bold(),
yes = "y".green(), yes = "y".green(),
); );

View file

@ -1,5 +1,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use crossterm::style::Stylize;
use crate::{ use crate::{
action::{Action, ActionDescription}, action::{Action, ActionDescription},
planner::Planner, planner::Planner,
@ -15,23 +17,37 @@ pub struct InstallPlan {
impl InstallPlan { impl InstallPlan {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub fn describe_execute(&self, explain: bool) -> String { pub fn describe_execute(
&self,
explain: bool,
) -> Result<String, Box<dyn std::error::Error + Sync + Send>> {
let Self { planner, actions } = self; let Self { planner, actions } = self;
format!( let buf = format!(
"\ "\
This Nix install is for:\n\ Nix install plan\n\
Operating System: {os_type}\n\
Init system: {init_type}\n\
Nix channels: {nix_channels}\n\
\n\ \n\
Created by planner: {planner:?} Planner: {planner}\n\
\n\ \n\
The following actions will be taken:\n\ Planner settings:\n\
{actions} \n\
{plan_settings}\n\
\n\
The following actions will be taken{maybe_explain}:\n\
\n\
{actions}\n\
", ",
os_type = "Linux", maybe_explain = if !explain {
init_type = "systemd", " (`--explain` for more context)"
nix_channels = "todo", } else {
""
},
planner = planner.typetag_name(),
plan_settings = planner
.describe()?
.into_iter()
.map(|(k, v)| format!("* {k}: {v}", k = k.bold().white()))
.collect::<Vec<_>>()
.join("\n"),
actions = actions actions = actions
.iter() .iter()
.map(|v| v.describe_execute()) .map(|v| v.describe_execute())
@ -43,17 +59,18 @@ impl InstallPlan {
} = desc; } = desc;
let mut buf = String::default(); let mut buf = String::default();
buf.push_str(&format!("* {description}\n")); buf.push_str(&format!("* {description}"));
if explain { if explain {
for line in explanation { for line in explanation {
buf.push_str(&format!(" {line}\n")); buf.push_str(&format!("\n {line}"));
} }
} }
buf buf
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n"), .join("\n"),
) );
Ok(buf)
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]

View file

@ -1,4 +1,4 @@
use std::io::Cursor; use std::{collections::HashMap, io::Cursor};
use clap::ArgAction; use clap::ArgAction;
use tokio::process::Command; use tokio::process::Command;
@ -97,6 +97,28 @@ impl Planner for DarwinMulti {
], ],
}) })
} }
fn describe(
&self,
) -> Result<HashMap<String, serde_json::Value>, Box<dyn std::error::Error + Sync + Send>> {
let Self {
settings,
volume_encrypt,
volume_label,
root_disk,
} = self;
let mut map = HashMap::default();
map.extend(settings.describe()?.into_iter());
map.insert(
"volume_encrypt".into(),
serde_json::to_value(volume_encrypt)?,
);
map.insert("volume_label".into(), serde_json::to_value(volume_label)?);
map.insert("root_disk".into(), serde_json::to_value(root_disk)?);
Ok(map)
}
} }
impl Into<BuiltinPlanner> for DarwinMulti { impl Into<BuiltinPlanner> for DarwinMulti {

View file

@ -1,3 +1,5 @@
use std::collections::HashMap;
use crate::{ use crate::{
action::{ action::{
common::{ConfigureNix, CreateDirectory, ProvisionNix}, common::{ConfigureNix, CreateDirectory, ProvisionNix},
@ -33,6 +35,17 @@ impl Planner for LinuxMulti {
], ],
}) })
} }
fn describe(
&self,
) -> Result<HashMap<String, serde_json::Value>, Box<dyn std::error::Error + Sync + Send>> {
let Self { settings } = self;
let mut map = HashMap::default();
map.extend(settings.describe()?.into_iter());
Ok(map)
}
} }
impl Into<BuiltinPlanner> for LinuxMulti { impl Into<BuiltinPlanner> for LinuxMulti {

View file

@ -2,6 +2,8 @@ pub mod darwin;
pub mod linux; pub mod linux;
pub mod specific; pub mod specific;
use std::collections::HashMap;
use crate::{settings::InstallSettingsError, BoxableError, InstallPlan}; use crate::{settings::InstallSettingsError, BoxableError, InstallPlan};
#[async_trait::async_trait] #[async_trait::async_trait]
@ -11,6 +13,9 @@ pub trait Planner: std::fmt::Debug + Send + Sync + dyn_clone::DynClone {
where where
Self: Sized; Self: Sized;
async fn plan(self) -> Result<InstallPlan, Box<dyn std::error::Error + Sync + Send>>; async fn plan(self) -> Result<InstallPlan, Box<dyn std::error::Error + Sync + Send>>;
fn describe(
&self,
) -> Result<HashMap<String, serde_json::Value>, Box<dyn std::error::Error + Sync + Send>>;
} }
dyn_clone::clone_trait_object!(Planner); dyn_clone::clone_trait_object!(Planner);

View file

@ -1,3 +1,7 @@
use std::collections::HashMap;
use serde_json::json;
use crate::{ use crate::{
action::{ action::{
common::{CreateDirectory, ProvisionNix}, common::{CreateDirectory, ProvisionNix},
@ -33,6 +37,17 @@ impl Planner for SteamDeck {
], ],
}) })
} }
fn describe(
&self,
) -> Result<HashMap<String, serde_json::Value>, Box<dyn std::error::Error + Sync + Send>> {
let Self { settings } = self;
let mut map = HashMap::default();
map.extend(settings.describe()?.into_iter());
Ok(map)
}
} }
impl Into<BuiltinPlanner> for SteamDeck { impl Into<BuiltinPlanner> for SteamDeck {

View file

@ -1,6 +1,10 @@
use std::collections::HashMap;
use clap::ArgAction; use clap::ArgAction;
use url::Url; use url::Url;
use crate::cli::arg::ChannelValue;
pub const NIX_X64_64_LINUX_URL: &str = pub const NIX_X64_64_LINUX_URL: &str =
"https://releases.nixos.org/nix/nix-2.11.0/nix-2.11.0-x86_64-linux.tar.xz"; "https://releases.nixos.org/nix/nix-2.11.0/nix-2.11.0-x86_64-linux.tar.xz";
pub const NIX_AARCH64_LINUX_URL: &str = pub const NIX_AARCH64_LINUX_URL: &str =
@ -145,6 +149,66 @@ impl CommonSettings {
force: false, force: false,
}) })
} }
pub fn describe(
&self,
) -> Result<HashMap<String, serde_json::Value>, Box<dyn std::error::Error + Sync + Send>> {
let Self {
channels,
modify_profile,
daemon_user_count,
nix_build_group_name,
nix_build_group_id,
nix_build_user_prefix,
nix_build_user_id_base,
nix_package_url,
extra_conf,
force,
} = self;
let mut map = HashMap::default();
map.insert(
"channels".into(),
serde_json::to_value(
channels
.iter()
.map(|ChannelValue(k, v)| format!("{k}={v}"))
.collect::<Vec<_>>(),
)?,
);
map.insert(
"modify_profile".into(),
serde_json::to_value(modify_profile)?,
);
map.insert(
"daemon_user_count".into(),
serde_json::to_value(daemon_user_count)?,
);
map.insert(
"nix_build_group_name".into(),
serde_json::to_value(nix_build_group_name)?,
);
map.insert(
"nix_build_group_id".into(),
serde_json::to_value(nix_build_group_id)?,
);
map.insert(
"nix_build_user_prefix".into(),
serde_json::to_value(nix_build_user_prefix)?,
);
map.insert(
"nix_build_user_id_base".into(),
serde_json::to_value(nix_build_user_id_base)?,
);
map.insert(
"nix_package_url".into(),
serde_json::to_value(nix_package_url)?,
);
map.insert("extra_conf".into(), serde_json::to_value(extra_conf)?);
map.insert("force".into(), serde_json::to_value(force)?);
Ok(map)
}
} }
// Builder Pattern // Builder Pattern