From 199ca7f91179b92a0c599387ba93197fffc2475a Mon Sep 17 00:00:00 2001 From: Ana Hobden Date: Fri, 23 Sep 2022 16:22:50 -0700 Subject: [PATCH] It buildsgit add src/git add src/ Signed-off-by: Ana Hobden --- .../base/configure_nix_daemon_service.rs | 57 ++++--- src/actions/base/create_directory.rs | 50 ++++-- src/actions/base/create_file.rs | 60 ++++--- src/actions/base/create_group.rs | 30 ++-- src/actions/base/create_or_append_file.rs | 54 +++--- src/actions/base/create_user.rs | 25 ++- src/actions/base/fetch_nix.rs | 43 +++-- src/actions/base/move_unpacked_nix.rs | 33 ++-- .../base/place_channel_configuration.rs | 24 +-- src/actions/base/place_nix_configuration.rs | 27 +-- src/actions/base/setup_default_profile.rs | 51 +++--- src/actions/base/start_systemd_unit.rs | 40 ++--- src/actions/meta/configure_nix.rs | 54 +++--- src/actions/meta/configure_shell_profile.rs | 44 ++--- src/actions/meta/create_nix_tree.rs | 28 ++-- src/actions/meta/create_users_and_group.rs | 68 +++++--- src/actions/meta/provision_nix.rs | 59 ++++--- src/actions/meta/start_nix_daemon.rs | 52 ++---- src/actions/mod.rs | 158 ++++++++---------- src/cli/mod.rs | 4 +- src/lib.rs | 4 +- src/plan.rs | 6 +- 22 files changed, 515 insertions(+), 456 deletions(-) diff --git a/src/actions/base/configure_nix_daemon_service.rs b/src/actions/base/configure_nix_daemon_service.rs index a685808..273ba1c 100644 --- a/src/actions/base/configure_nix_daemon_service.rs +++ b/src/actions/base/configure_nix_daemon_service.rs @@ -5,7 +5,7 @@ use tokio::process::Command; use crate::{execute_command, HarmonicError}; -use crate::actions::{ActionDescription, Actionable, ActionState, Action}; +use crate::actions::{ActionDescription, Actionable, ActionState, Action, ActionError}; const SERVICE_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.service"; const SOCKET_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.socket"; @@ -13,20 +13,24 @@ const TMPFILES_SRC: &str = "/nix/var/nix/profiles/default//lib/tmpfiles.d/nix-da const TMPFILES_DEST: &str = "/etc/tmpfiles.d/nix-daemon.conf"; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] -pub struct ConfigureNixDaemonService {} +pub struct ConfigureNixDaemonService { + action_state: ActionState, +} impl ConfigureNixDaemonService { #[tracing::instrument(skip_all)] - pub async fn plan() -> Result { + pub async fn plan() -> Result { if !Path::new("/run/systemd/system").exists() { - return Err(HarmonicError::InitNotSupported); + return Err(ConfigureNixDaemonServiceError::InitNotSupported); } - Ok(Self {}) + Ok(Self { + action_state: ActionState::Planned, + }) } } #[async_trait::async_trait] -impl Actionable for ActionState { +impl Actionable for ConfigureNixDaemonService { type Error = ConfigureNixDaemonServiceError; fn description(&self) -> Vec { vec![ActionDescription::new( @@ -42,33 +46,33 @@ impl Actionable for ActionState { #[tracing::instrument(skip_all)] async fn execute(&mut self) -> Result<(), Self::Error> { + let Self { action_state } = self; tracing::info!("Configuring nix daemon service"); tracing::trace!(src = TMPFILES_SRC, dest = TMPFILES_DEST, "Symlinking"); tokio::fs::symlink(TMPFILES_SRC, TMPFILES_DEST) .await .map_err(|e| { - HarmonicError::Symlink(PathBuf::from(TMPFILES_SRC), PathBuf::from(TMPFILES_DEST), e) + Self::Error::Symlink(PathBuf::from(TMPFILES_SRC), PathBuf::from(TMPFILES_DEST), e) })?; execute_command( Command::new("systemd-tmpfiles") .arg("--create") .arg("--prefix=/nix/var/nix"), - false, ) - .await?; + .await.map_err(Self::Error::CommandFailed)?; execute_command( Command::new("systemctl").arg("link").arg(SERVICE_SRC), - false, ) - .await?; + .await.map_err(Self::Error::CommandFailed)?; - execute_command(Command::new("systemctl").arg("link").arg(SOCKET_SRC), false).await?; + execute_command(Command::new("systemctl").arg("link").arg(SOCKET_SRC)).await.map_err(Self::Error::CommandFailed)?; - execute_command(Command::new("systemctl").arg("daemon-reload"), false).await?; + execute_command(Command::new("systemctl").arg("daemon-reload")).await.map_err(Self::Error::CommandFailed)?; + *action_state = ActionState::Completed; Ok(()) } @@ -83,17 +87,28 @@ impl Actionable for ActionState { -impl From> for ActionState { - fn from(v: ActionState) -> Self { - match v { - ActionState::Completed(_) => ActionState::Completed(Action::ConfigureNixDaemonService(v)), - ActionState::Planned(_) => ActionState::Planned(Action::ConfigureNixDaemonService(v)), - ActionState::Reverted(_) => ActionState::Reverted(Action::ConfigureNixDaemonService(v)), - } +impl From for Action { + fn from(v: ConfigureNixDaemonService) -> Self { + Action::ConfigureNixDaemonService(v) } } #[derive(Debug, thiserror::Error, Serialize)] pub enum ConfigureNixDaemonServiceError { - + #[error("Symlinking from `{0}` to `{1}`")] + Symlink( + std::path::PathBuf, + std::path::PathBuf, + #[source] + #[serde(serialize_with = "crate::serialize_error_to_display")] + std::io::Error + ), + #[error("Command `{0}` failed to execute")] + CommandFailed( + #[source] + #[serde(serialize_with = "crate::serialize_error_to_display")] + std::io::Error + ), + #[error("No supported init system found")] + InitNotSupported, } diff --git a/src/actions/base/create_directory.rs b/src/actions/base/create_directory.rs index 1865039..2fc94b7 100644 --- a/src/actions/base/create_directory.rs +++ b/src/actions/base/create_directory.rs @@ -6,7 +6,7 @@ use tokio::fs::create_dir; use crate::HarmonicError; -use crate::actions::{ActionDescription, Actionable, ActionState, Action}; +use crate::actions::{ActionDescription, Actionable, ActionState, Action, ActionError}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct CreateDirectory { @@ -14,6 +14,7 @@ pub struct CreateDirectory { user: String, group: String, mode: u32, + action_state: ActionState, } impl CreateDirectory { @@ -24,11 +25,11 @@ impl CreateDirectory { group: String, mode: u32, force: bool, - ) -> Result { + ) -> Result { let path = path.as_ref(); if path.exists() && !force { - return Err(HarmonicError::CreateDirectory( + return Err(CreateDirectoryError::Exists( path.to_path_buf(), std::io::Error::new( std::io::ErrorKind::AlreadyExists, @@ -42,12 +43,13 @@ impl CreateDirectory { user, group, mode, + action_state: ActionState::Planned, }) } } #[async_trait::async_trait] -impl Actionable for ActionState { +impl Actionable for CreateDirectory { type Error = CreateDirectoryError; fn description(&self) -> Vec { let Self { @@ -55,6 +57,7 @@ impl Actionable for ActionState { user, group, mode, + action_state, } = &self; vec![ActionDescription::new( format!("Create the directory `{}`", path.display()), @@ -72,23 +75,25 @@ impl Actionable for ActionState { user, group, mode, + action_state, } = self; let gid = Group::from_name(group.as_str()) - .map_err(|e| HarmonicError::GroupId(group.clone(), e))? - .ok_or(HarmonicError::NoGroup(group.clone()))? + .map_err(|e| Self::Error::GroupId(group.clone(), e))? + .ok_or(Self::Error::NoGroup(group.clone()))? .gid; let uid = User::from_name(user.as_str()) - .map_err(|e| HarmonicError::UserId(user.clone(), e))? - .ok_or(HarmonicError::NoUser(user.clone()))? + .map_err(|e| Self::Error::UserId(user.clone(), e))? + .ok_or(Self::Error::NoUser(user.clone()))? .uid; tracing::trace!(path = %path.display(), "Creating directory"); create_dir(path.clone()) .await - .map_err(|e| HarmonicError::CreateDirectory(path.clone(), e))?; - chown(&path, Some(uid), Some(gid)).map_err(|e| HarmonicError::Chown(path.clone(), e))?; + .map_err(|e| Self::Error::Creating(path.clone(), e))?; + chown(path, Some(uid), Some(gid)).map_err(|e| Self::Error::Chown(path.clone(), e))?; + *action_state = ActionState::Completed; Ok(()) } @@ -102,18 +107,27 @@ impl Actionable for ActionState { } -impl From> for ActionState { - fn from(v: ActionState) -> Self { - match v { - ActionState::Completed(_) => ActionState::Completed(Action::CreateDirectory(v)), - ActionState::Planned(_) => ActionState::Planned(Action::CreateDirectory(v)), - ActionState::Reverted(_) => ActionState::Reverted(Action::CreateDirectory(v)), - } +impl From for Action { + fn from(v: CreateDirectory) -> Self { + Action::CreateDirectory(v) } } #[derive(Debug, thiserror::Error, Serialize)] pub enum CreateDirectoryError { - + #[error("Directory exists `{0}`")] + Exists(std::path::PathBuf, #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] std::io::Error), + #[error("Creating directory `{0}`")] + Creating(std::path::PathBuf, #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] std::io::Error), + #[error("Chowning directory `{0}`")] + Chown(std::path::PathBuf, #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] nix::errno::Errno), + #[error("Getting uid for user `{0}`")] + UserId(String, #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] nix::errno::Errno), + #[error("Getting user `{0}`")] + NoUser(String), + #[error("Getting gid for group `{0}`")] + GroupId(String, #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] nix::errno::Errno), + #[error("Getting group `{0}`")] + NoGroup(String), } diff --git a/src/actions/base/create_file.rs b/src/actions/base/create_file.rs index e4e43bc..3cfac1e 100644 --- a/src/actions/base/create_file.rs +++ b/src/actions/base/create_file.rs @@ -6,7 +6,7 @@ use tokio::{ io::AsyncWriteExt, }; -use crate::{HarmonicError, actions::{ActionState, Action}}; +use crate::{HarmonicError, actions::{ActionState, Action, ActionError}}; use crate::actions::{ActionDescription, Actionable}; @@ -18,6 +18,7 @@ pub struct CreateFile { mode: u32, buf: String, force: bool, + action_state: ActionState, } impl CreateFile { @@ -29,17 +30,11 @@ impl CreateFile { mode: u32, buf: String, force: bool, - ) -> Result { + ) -> Result { let path = path.as_ref().to_path_buf(); if path.exists() && !force { - return Err(HarmonicError::CreateFile( - path.to_path_buf(), - std::io::Error::new( - std::io::ErrorKind::AlreadyExists, - format!("Directory `{}` already exists", path.display()), - ), - )); + return Err(CreateFileError::Exists(path.to_path_buf())); } Ok(Self { @@ -49,12 +44,13 @@ impl CreateFile { mode, buf, force, + action_state: ActionState::Planned, }) } } #[async_trait::async_trait] -impl Actionable for ActionState { +impl Actionable for CreateFile { type Error = CreateFileError; fn description(&self) -> Vec { let Self { @@ -64,6 +60,7 @@ impl Actionable for ActionState { mode, buf, force, + action_state: _, } = &self; vec![ActionDescription::new( format!("Create or overwrite file `{}`", path.display()), @@ -82,6 +79,7 @@ impl Actionable for ActionState { mode, buf, force: _, + action_state, } = self; tracing::trace!(path = %path.display(), "Creating file"); let mut file = OpenOptions::new() @@ -90,24 +88,25 @@ impl Actionable for ActionState { .read(true) .open(&path) .await - .map_err(|e| HarmonicError::OpenFile(path.to_owned(), e))?; + .map_err(|e| Self::Error::OpenFile(path.to_owned(), e))?; file.write_all(buf.as_bytes()) .await - .map_err(|e| HarmonicError::WriteFile(path.to_owned(), e))?; + .map_err(|e| Self::Error::WriteFile(path.to_owned(), e))?; let gid = Group::from_name(group.as_str()) - .map_err(|e| HarmonicError::GroupId(group.clone(), e))? - .ok_or(HarmonicError::NoGroup(group.clone()))? + .map_err(|e| Self::Error::GroupId(group.clone(), e))? + .ok_or(Self::Error::NoGroup(group.clone()))? .gid; let uid = User::from_name(user.as_str()) - .map_err(|e| HarmonicError::UserId(user.clone(), e))? - .ok_or(HarmonicError::NoUser(user.clone()))? + .map_err(|e| Self::Error::UserId(user.clone(), e))? + .ok_or(Self::Error::NoUser(user.clone()))? .uid; tracing::trace!(path = %path.display(), "Chowning file"); - chown(&path, Some(uid), Some(gid)).map_err(|e| HarmonicError::Chown(path.clone(), e))?; + chown(path, Some(uid), Some(gid)).map_err(|e| Self::Error::Chown(path.clone(), e))?; + *action_state = ActionState::Completed; Ok(()) } @@ -120,17 +119,28 @@ impl Actionable for ActionState { } } -impl From> for ActionState { - fn from(v: ActionState) -> Self { - match v { - ActionState::Completed(_) => ActionState::Completed(Action::CreateFile(v)), - ActionState::Planned(_) => ActionState::Planned(Action::CreateFile(v)), - ActionState::Reverted(_) => ActionState::Reverted(Action::CreateFile(v)), - } +impl From for Action { + fn from(v: CreateFile) -> Self { + Action::CreateFile(v) } } #[derive(Debug, thiserror::Error, Serialize)] pub enum CreateFileError { - + #[error("File exists `{0}`")] + Exists(std::path::PathBuf), + #[error("Open file `{0}`")] + OpenFile(std::path::PathBuf, #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] std::io::Error), + #[error("Write file `{0}`")] + WriteFile(std::path::PathBuf, #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] std::io::Error), + #[error("Getting uid for user `{0}`")] + UserId(String, #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] nix::errno::Errno), + #[error("Getting user `{0}`")] + NoUser(String), + #[error("Getting gid for group `{0}`")] + GroupId(String, #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] nix::errno::Errno), + #[error("Getting group `{0}`")] + NoGroup(String), + #[error("Chowning directory `{0}`")] + Chown(std::path::PathBuf, #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] nix::errno::Errno), } diff --git a/src/actions/base/create_group.rs b/src/actions/base/create_group.rs index 8e7417e..a3aa6bc 100644 --- a/src/actions/base/create_group.rs +++ b/src/actions/base/create_group.rs @@ -3,26 +3,27 @@ use tokio::process::Command; use crate::{HarmonicError, execute_command}; -use crate::actions::{ActionDescription, Actionable, ActionState, Action}; +use crate::actions::{ActionDescription, Actionable, ActionState, Action, ActionError}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct CreateGroup { name: String, gid: usize, + action_state: ActionState, } impl CreateGroup { #[tracing::instrument(skip_all)] pub fn plan(name: String, gid: usize) -> Self { - Self { name, gid } + Self { name, gid, action_state: ActionState::Planned } } } #[async_trait::async_trait] -impl Actionable for ActionState { - type Error = CreateOrAppendFileError; +impl Actionable for CreateGroup { + type Error = CreateGroupError; fn description(&self) -> Vec { - let Self { name, gid } = &self; + let Self { name, gid, action_state: _ } = &self; vec![ActionDescription::new( format!("Create group {name} with GID {gid}"), vec![format!( @@ -33,13 +34,14 @@ impl Actionable for ActionState { #[tracing::instrument(skip_all)] async fn execute(&mut self) -> Result<(), Self::Error> { - let Self { name, gid } = self; + let Self { name, gid, action_state } = self; execute_command( Command::new("groupadd").args(["-g", &gid.to_string(), "--system", &name]), - false, - ).await?; + ).await.map_err(CreateGroupError::Command)?; + + *action_state = ActionState::Completed; Ok(()) } @@ -51,16 +53,14 @@ impl Actionable for ActionState { } } -impl From> for ActionState { - fn from(v: ActionState) -> Self { - match v { - ActionState::Completed(_) => ActionState::Completed(Action::CreateGroup(v)), - ActionState::Planned(_) => ActionState::Planned(Action::CreateGroup(v)), - ActionState::Reverted(_) => ActionState::Reverted(Action::CreateGroup(v)), - } +impl From for Action { + fn from(v: CreateGroup) -> Self { + Action::CreateGroup(v) } } #[derive(Debug, thiserror::Error, Serialize)] pub enum CreateGroupError { + #[error("Failed to execute command")] + Command(#[source] #[serde(serialize_with = "crate::serialize_error_to_display")] std::io::Error) } \ No newline at end of file diff --git a/src/actions/base/create_or_append_file.rs b/src/actions/base/create_or_append_file.rs index a9fe38f..2155385 100644 --- a/src/actions/base/create_or_append_file.rs +++ b/src/actions/base/create_or_append_file.rs @@ -9,7 +9,7 @@ use tokio::{ io::{AsyncSeekExt, AsyncWriteExt}, }; -use crate::{HarmonicError, actions::{ActionState, Action}}; +use crate::{HarmonicError, actions::{ActionState, Action, ActionError}}; use crate::actions::{ActionDescription, Actionable}; @@ -20,6 +20,7 @@ pub struct CreateOrAppendFile { group: String, mode: u32, buf: String, + action_state: ActionState, } impl CreateOrAppendFile { @@ -30,7 +31,7 @@ impl CreateOrAppendFile { group: String, mode: u32, buf: String, - ) -> Result { + ) -> Result { let path = path.as_ref().to_path_buf(); Ok(Self { @@ -39,12 +40,13 @@ impl CreateOrAppendFile { group, mode, buf, + action_state: ActionState::Planned, }) } } #[async_trait::async_trait] -impl Actionable for ActionState { +impl Actionable for CreateOrAppendFile { type Error = CreateOrAppendFileError; fn description(&self) -> Vec { let Self { @@ -53,6 +55,7 @@ impl Actionable for ActionState { group, mode, buf, + action_state: _, } = &self; vec![ActionDescription::new( format!("Create or append file `{}`", path.display()), @@ -70,6 +73,7 @@ impl Actionable for ActionState { group, mode, buf, + action_state, } = self; tracing::trace!(path = %path.display(), "Creating or appending"); @@ -79,27 +83,28 @@ impl Actionable for ActionState { .read(true) .open(&path) .await - .map_err(|e| HarmonicError::OpenFile(path.to_owned(), e))?; + .map_err(|e| Self::Error::OpenFile(path.to_owned(), e))?; file.seek(SeekFrom::End(0)) .await - .map_err(|e| HarmonicError::SeekFile(path.to_owned(), e))?; + .map_err(|e| Self::Error::SeekFile(path.to_owned(), e))?; file.write_all(buf.as_bytes()) .await - .map_err(|e| HarmonicError::WriteFile(path.to_owned(), e))?; + .map_err(|e| Self::Error::WriteFile(path.to_owned(), e))?; let gid = Group::from_name(group.as_str()) - .map_err(|e| HarmonicError::GroupId(group.clone(), e))? - .ok_or(HarmonicError::NoGroup(group.clone()))? + .map_err(|e| Self::Error::GroupId(group.clone(), e))? + .ok_or(Self::Error::NoGroup(group.clone()))? .gid; let uid = User::from_name(user.as_str()) - .map_err(|e| HarmonicError::UserId(user.clone(), e))? - .ok_or(HarmonicError::NoUser(user.clone()))? + .map_err(|e| Self::Error::UserId(user.clone(), e))? + .ok_or(Self::Error::NoUser(user.clone()))? .uid; tracing::trace!(path = %path.display(), "Chowning"); - chown(&path, Some(uid), Some(gid)).map_err(|e| HarmonicError::Chown(path.clone(), e))?; + chown(path, Some(uid), Some(gid)).map_err(|e| Self::Error::Chown(path.clone(), e))?; + *action_state = ActionState::Completed; Ok(()) } @@ -113,17 +118,28 @@ impl Actionable for ActionState { } -impl From> for ActionState { - fn from(v: ActionState) -> Self { - match v { - ActionState::Completed(_) => ActionState::Completed(Action::CreateOrAppendFile(v)), - ActionState::Planned(_) => ActionState::Planned(Action::CreateOrAppendFile(v)), - ActionState::Reverted(_) => ActionState::Reverted(Action::CreateOrAppendFile(v)), - } +impl From for Action { + fn from(v: CreateOrAppendFile) -> Self { + Action::CreateOrAppendFile(v) } } #[derive(Debug, thiserror::Error, Serialize)] pub enum CreateOrAppendFileError { - + #[error("Open file `{0}`")] + OpenFile(std::path::PathBuf, #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] std::io::Error), + #[error("Write file `{0}`")] + WriteFile(std::path::PathBuf, #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] std::io::Error), + #[error("Seek file `{0}`")] + SeekFile(std::path::PathBuf, #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] std::io::Error), + #[error("Getting uid for user `{0}`")] + UserId(String, #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] nix::errno::Errno), + #[error("Getting user `{0}`")] + NoUser(String), + #[error("Getting gid for group `{0}`")] + GroupId(String, #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] nix::errno::Errno), + #[error("Getting group `{0}`")] + NoGroup(String), + #[error("Chowning directory `{0}`")] + Chown(std::path::PathBuf, #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] nix::errno::Errno), } diff --git a/src/actions/base/create_user.rs b/src/actions/base/create_user.rs index 5f9fa63..74418ef 100644 --- a/src/actions/base/create_user.rs +++ b/src/actions/base/create_user.rs @@ -3,24 +3,25 @@ use tokio::process::Command; use crate::{HarmonicError, execute_command}; -use crate::actions::{ActionDescription, Actionable, ActionState, Action}; +use crate::actions::{ActionDescription, Actionable, ActionState, Action, ActionError}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct CreateUser { name: String, uid: usize, gid: usize, + action_state: ActionState, } impl CreateUser { #[tracing::instrument(skip_all)] pub fn plan(name: String, uid: usize, gid: usize) -> Self { - Self { name, uid, gid } + Self { name, uid, gid, action_state: ActionState::Planned } } } #[async_trait::async_trait] -impl Actionable for ActionState { +impl Actionable for CreateUser { type Error = CreateUserError; fn description(&self) -> Vec { let name = &self.name; @@ -35,7 +36,7 @@ impl Actionable for ActionState { #[tracing::instrument(skip_all)] async fn execute(&mut self) -> Result<(), Self::Error> { - let Self { name, uid, gid } = self; + let Self { name, uid, gid, action_state } = self; execute_command(Command::new("useradd").args([ "--home-dir", @@ -55,8 +56,9 @@ impl Actionable for ActionState { "--password", "\"!\"", &name.to_string(), - ]), false).await?; + ])).await.map_err(Self::Error::Command)?; + *action_state = ActionState::Completed; Ok(()) } @@ -69,17 +71,14 @@ impl Actionable for ActionState { } } -impl From> for ActionState { - fn from(v: ActionState) -> Self { - match v { - ActionState::Completed(_) => ActionState::Completed(Action::CreateUser(v)), - ActionState::Planned(_) => ActionState::Planned(Action::CreateUser(v)), - ActionState::Reverted(_) => ActionState::Reverted(Action::CreateUser(v)), - } +impl From for Action { + fn from(v: CreateUser) -> Self { + Action::CreateUser(v) } } #[derive(Debug, thiserror::Error, Serialize)] pub enum CreateUserError { - + #[error("Failed to execute command")] + Command(#[source] #[serde(serialize_with = "crate::serialize_error_to_display")] std::io::Error) } diff --git a/src/actions/base/fetch_nix.rs b/src/actions/base/fetch_nix.rs index 2c2b1c0..fa2cd54 100644 --- a/src/actions/base/fetch_nix.rs +++ b/src/actions/base/fetch_nix.rs @@ -3,16 +3,17 @@ use std::path::{PathBuf}; use bytes::Buf; use reqwest::Url; use serde::Serialize; -use tokio::task::spawn_blocking; +use tokio::task::{spawn_blocking, JoinError}; use crate::HarmonicError; -use crate::actions::{ActionDescription, Actionable, ActionState, Action}; +use crate::actions::{ActionDescription, Actionable, ActionState, Action, ActionError}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct FetchNix { url: Url, destination: PathBuf, + action_state: ActionState, } impl FetchNix { @@ -21,15 +22,15 @@ impl FetchNix { // TODO(@hoverbear): Check URL exists? // TODO(@hoverbear): Check tempdir exists - Ok(Self { url, destination }) + Ok(Self { url, destination, action_state: ActionState::Planned }) } } #[async_trait::async_trait] -impl Actionable for ActionState { +impl Actionable for FetchNix { type Error = FetchNixError; fn description(&self) -> Vec { - let Self { url, destination } = &self; + let Self { url, destination, action_state: _ } = &self; vec![ActionDescription::new( format!("Fetch Nix from `{url}`"), vec![format!( @@ -41,20 +42,20 @@ impl Actionable for ActionState { #[tracing::instrument(skip_all)] async fn execute(&mut self) -> Result<(), Self::Error> { - let Self { url, destination } = self; + let Self { url, destination, action_state } = self; tracing::trace!(%url, "Fetching url"); let res = reqwest::get(url.clone()) .await - .map_err(HarmonicError::Reqwest)?; - let bytes = res.bytes().await.map_err(HarmonicError::Reqwest)?; + .map_err(Self::Error::Reqwest)?; + let bytes = res.bytes().await.map_err(Self::Error::Reqwest)?; // TODO(@Hoverbear): Pick directory tracing::trace!("Unpacking tar.xz"); let destination_clone = destination.clone(); - let handle: Result<(), HarmonicError> = spawn_blocking(move || { + let handle: Result<(), Self::Error> = spawn_blocking(move || { let decoder = xz2::read::XzDecoder::new(bytes.reader()); let mut archive = tar::Archive::new(decoder); - archive.unpack(&destination_clone).map_err(HarmonicError::Unarchive)?; + archive.unpack(&destination_clone).map_err(Self::Error::Unarchive)?; tracing::debug!(destination = %destination_clone.display(), "Downloaded & extracted Nix"); Ok(()) }) @@ -62,6 +63,7 @@ impl Actionable for ActionState { handle?; + *action_state = ActionState::Completed; Ok(()) } @@ -74,17 +76,22 @@ impl Actionable for ActionState { } } -impl From> for ActionState { - fn from(v: ActionState) -> Self { - match v { - ActionState::Completed(_) => ActionState::Completed(Action::FetchNix(v)), - ActionState::Planned(_) => ActionState::Planned(Action::FetchNix(v)), - ActionState::Reverted(_) => ActionState::Reverted(Action::FetchNix(v)), - } +impl From for Action { + fn from(v: FetchNix) -> Self { + Action::FetchNix(v) } } #[derive(Debug, thiserror::Error, Serialize)] pub enum FetchNixError { - + #[error(transparent)] + Join( + #[from] + #[serde(serialize_with = "crate::serialize_error_to_display")] + JoinError + ), + #[error("Request error")] + Reqwest(#[from] #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] reqwest::Error), + #[error("Unarchiving error")] + Unarchive(#[source] #[serde(serialize_with = "crate::serialize_error_to_display")] std::io::Error), } diff --git a/src/actions/base/move_unpacked_nix.rs b/src/actions/base/move_unpacked_nix.rs index 06438a4..d0edbc0 100644 --- a/src/actions/base/move_unpacked_nix.rs +++ b/src/actions/base/move_unpacked_nix.rs @@ -4,26 +4,27 @@ use serde::Serialize; use crate::HarmonicError; -use crate::actions::{ActionDescription, Actionable, ActionState, Action}; +use crate::actions::{ActionDescription, Actionable, ActionState, Action, ActionError}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct MoveUnpackedNix { source: PathBuf, + action_state: ActionState, } impl MoveUnpackedNix { #[tracing::instrument(skip_all)] - pub async fn plan(source: PathBuf) -> Result { + pub async fn plan(source: PathBuf) -> Result { // Note: Do NOT try to check for the source/dest since the installer creates those - Ok(Self { source }) + Ok(Self { source, action_state: ActionState::Planned }) } } #[async_trait::async_trait] -impl Actionable for ActionState { +impl Actionable for MoveUnpackedNix { type Error = MoveUnpackedNixError; fn description(&self) -> Vec { - let Self { source } = &self; + let Self { source, action_state: _ } = &self; vec![ActionDescription::new( format!("Move the downloaded Nix into `/nix`"), vec![format!( @@ -35,7 +36,7 @@ impl Actionable for ActionState { #[tracing::instrument(skip_all)] async fn execute(&mut self) -> Result<(), Self::Error> { - let Self { source } = self; + let Self { source, action_state } = self; // TODO(@Hoverbear): I would like to make this less awful let found_nix_paths = @@ -51,8 +52,9 @@ impl Actionable for ActionState { let dest = Path::new("/nix/store"); tokio::fs::rename(src.clone(), dest) .await - .map_err(|e| HarmonicError::Rename(src, dest.to_owned(), e))?; + .map_err(|e| MoveUnpackedNixError::Rename(src, dest.to_owned(), e))?; + *action_state = ActionState::Completed; Ok(()) } @@ -65,17 +67,18 @@ impl Actionable for ActionState { } } -impl From> for ActionState { - fn from(v: ActionState) -> Self { - match v { - ActionState::Completed(_) => ActionState::Completed(Action::MoveUnpackedNix(v)), - ActionState::Planned(_) => ActionState::Planned(Action::MoveUnpackedNix(v)), - ActionState::Reverted(_) => ActionState::Reverted(Action::MoveUnpackedNix(v)), - } +impl From for Action { + fn from(v: MoveUnpackedNix) -> Self { + Action::MoveUnpackedNix(v) } } #[derive(Debug, thiserror::Error, Serialize)] pub enum MoveUnpackedNixError { - + #[error("Glob pattern error")] + GlobPatternError(#[from] #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] glob::PatternError), + #[error("Glob globbing error")] + GlobGlobError(#[from] #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] glob::GlobError), + #[error("Rename `{0}` to `{1}`")] + Rename(std::path::PathBuf, std::path::PathBuf, #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] std::io::Error), } diff --git a/src/actions/base/place_channel_configuration.rs b/src/actions/base/place_channel_configuration.rs index f2dcc1b..d37f31c 100644 --- a/src/actions/base/place_channel_configuration.rs +++ b/src/actions/base/place_channel_configuration.rs @@ -3,7 +3,7 @@ use serde::Serialize; use crate::HarmonicError; -use crate::actions::{ActionDescription, Actionable, ActionState, Action}; +use crate::actions::{ActionDescription, Actionable, ActionState, Action, ActionError}; use super::{CreateFile, CreateFileError}; @@ -13,11 +13,12 @@ const NIX_CHANNELS_PATH: &str = "/root/.nix-channels"; pub struct PlaceChannelConfiguration { channels: Vec<(String, Url)>, create_file: CreateFile, + action_state: ActionState, } impl PlaceChannelConfiguration { #[tracing::instrument(skip_all)] - pub async fn plan(channels: Vec<(String, Url)>, force: bool) -> Result { + pub async fn plan(channels: Vec<(String, Url)>, force: bool) -> Result { let buf = channels .iter() .map(|(name, url)| format!("{} {}", url, name)) @@ -28,17 +29,19 @@ impl PlaceChannelConfiguration { Ok(Self { create_file, channels, + action_state: ActionState::Planned, }) } } #[async_trait::async_trait] -impl Actionable for ActionState { +impl Actionable for PlaceChannelConfiguration { type Error = PlaceChannelConfigurationError; fn description(&self) -> Vec { let Self { channels, create_file, + action_state: _, } = self; vec![ActionDescription::new( "Place a channel configuration".to_string(), @@ -51,10 +54,12 @@ impl Actionable for ActionState { let Self { create_file, channels, + action_state, } = self; create_file.execute().await?; + *action_state = ActionState::Completed; Ok(()) } @@ -67,18 +72,15 @@ impl Actionable for ActionState { } } -impl From> for ActionState { - fn from(v: ActionState) -> Self { - match v { - ActionState::Completed(_) => ActionState::Completed(Action::PlaceChannelConfiguration(v)), - ActionState::Planned(_) => ActionState::Planned(Action::PlaceChannelConfiguration(v)), - ActionState::Reverted(_) => ActionState::Reverted(Action::PlaceChannelConfiguration(v)), - } +impl From for Action { + fn from(v: PlaceChannelConfiguration) -> Self { + Action::PlaceChannelConfiguration(v) } } #[derive(Debug, thiserror::Error, Serialize)] pub enum PlaceChannelConfigurationError { - + #[error(transparent)] + CreateFile(#[from] CreateFileError), } diff --git a/src/actions/base/place_nix_configuration.rs b/src/actions/base/place_nix_configuration.rs index 898a605..213e715 100644 --- a/src/actions/base/place_nix_configuration.rs +++ b/src/actions/base/place_nix_configuration.rs @@ -2,7 +2,7 @@ use serde::Serialize; use crate::HarmonicError; -use crate::actions::{ActionDescription, Actionable, ActionState, Action}; +use crate::actions::{ActionDescription, Actionable, ActionState, Action, ActionError}; use super::{CreateFile, CreateFileError, CreateDirectory, CreateDirectoryError}; @@ -13,6 +13,7 @@ const NIX_CONF: &str = "/etc/nix/nix.conf"; pub struct PlaceNixConfiguration { create_directory: CreateDirectory, create_file: CreateFile, + action_state: ActionState, } impl PlaceNixConfiguration { @@ -21,7 +22,7 @@ impl PlaceNixConfiguration { nix_build_group_name: String, extra_conf: Option, force: bool, - ) -> Result { + ) -> Result { let buf = format!( "\ {extra_conf}\n\ @@ -32,12 +33,12 @@ impl PlaceNixConfiguration { let create_directory = CreateDirectory::plan(NIX_CONF_FOLDER, "root".into(), "root".into(), 0o0755, force).await?; let create_file = CreateFile::plan(NIX_CONF, "root".into(), "root".into(), 0o0664, buf, force).await?; - Ok(Self { create_directory, create_file }) + Ok(Self { create_directory, create_file, action_state: ActionState::Planned }) } } #[async_trait::async_trait] -impl Actionable for ActionState { +impl Actionable for PlaceNixConfiguration { type Error = PlaceNixConfigurationError; fn description(&self) -> Vec { @@ -49,11 +50,12 @@ impl Actionable for ActionState { #[tracing::instrument(skip_all)] async fn execute(&mut self) -> Result<(), Self::Error> { - let Self { create_file, create_directory } = self; + let Self { create_file, create_directory, action_state } = self; create_directory.execute().await?; create_file.execute().await?; + *action_state = ActionState::Completed; Ok(()) } @@ -66,18 +68,17 @@ impl Actionable for ActionState { } } -impl From> for ActionState { - fn from(v: ActionState) -> Self { - match v { - ActionState::Completed(_) => ActionState::Completed(Action::PlaceNixConfiguration(v)), - ActionState::Planned(_) => ActionState::Planned(Action::PlaceNixConfiguration(v)), - ActionState::Reverted(_) => ActionState::Reverted(Action::PlaceNixConfiguration(v)), - } +impl From for Action { + fn from(v: PlaceNixConfiguration) -> Self { + Action::PlaceNixConfiguration(v) } } #[derive(Debug, thiserror::Error, Serialize)] pub enum PlaceNixConfigurationError { - + #[error(transparent)] + CreateFile(#[from] CreateFileError), + #[error(transparent)] + CreateDirectory(#[from] CreateDirectoryError), } \ No newline at end of file diff --git a/src/actions/base/setup_default_profile.rs b/src/actions/base/setup_default_profile.rs index 29ed443..99a4376 100644 --- a/src/actions/base/setup_default_profile.rs +++ b/src/actions/base/setup_default_profile.rs @@ -1,4 +1,4 @@ -use crate::{execute_command, HarmonicError, actions::{ActionState, Action}}; +use crate::{execute_command, actions::{ActionState, Action, ActionError}}; use glob::glob; use serde::Serialize; @@ -13,13 +13,13 @@ pub struct SetupDefaultProfile { impl SetupDefaultProfile { #[tracing::instrument(skip_all)] - pub async fn plan(channels: Vec) -> Result { + pub async fn plan(channels: Vec) -> Result { Ok(Self { channels }) } } #[async_trait::async_trait] -impl Actionable for ActionState { +impl Actionable for SetupDefaultProfile { type Error = SetupDefaultProfileError; fn description(&self) -> Vec { vec![ActionDescription::new( @@ -36,7 +36,7 @@ impl Actionable for ActionState { // Find an `nix` package let nix_pkg_glob = "/nix/store/*-nix-*"; let mut found_nix_pkg = None; - for entry in glob(nix_pkg_glob).map_err(HarmonicError::GlobPatternError)? { + for entry in glob(nix_pkg_glob).map_err(Self::Error::GlobPatternError)? { match entry { Ok(path) => { // TODO(@Hoverbear): Should probably ensure is unique @@ -49,7 +49,7 @@ impl Actionable for ActionState { let nix_pkg = if let Some(nix_pkg) = found_nix_pkg { nix_pkg } else { - return Err(HarmonicError::NoNssCacert); // TODO(@hoverbear): Fix this error + return Err(Self::Error::NoNssCacert); // TODO(@hoverbear): Fix this error }; // Install `nix` itself into the store @@ -57,14 +57,13 @@ impl Actionable for ActionState { Command::new(nix_pkg.join("bin/nix-env")) .arg("-i") .arg(&nix_pkg), - false, ) - .await?; + .await.map_err(SetupDefaultProfileError::Command)?; // Find an `nss-cacert` package, add it too. let nss_ca_cert_pkg_glob = "/nix/store/*-nss-cacert-*"; let mut found_nss_ca_cert_pkg = None; - for entry in glob(nss_ca_cert_pkg_glob).map_err(HarmonicError::GlobPatternError)? { + for entry in glob(nss_ca_cert_pkg_glob).map_err(Self::Error::GlobPatternError)? { match entry { Ok(path) => { // TODO(@Hoverbear): Should probably ensure is unique @@ -77,17 +76,15 @@ impl Actionable for ActionState { let nss_ca_cert_pkg = if let Some(nss_ca_cert_pkg) = found_nss_ca_cert_pkg { nss_ca_cert_pkg } else { - return Err(HarmonicError::NoNssCacert); + return Err(Self::Error::NoNssCacert); }; // Install `nss-cacert` into the store execute_command( Command::new(nix_pkg.join("bin/nix-env")) .arg("-i") - .arg(&nss_ca_cert_pkg), - false, ) - .await?; + .await.map_err(SetupDefaultProfileError::Command)?; if !channels.is_empty() { let mut command = Command::new(nix_pkg.join("bin/nix-channel")); @@ -100,16 +97,7 @@ impl Actionable for ActionState { "/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt", ); - let command_str = format!("{:?}", command.as_std()); - let status = command - .status() - .await - .map_err(|e| HarmonicError::CommandFailedExec(command_str.clone(), e))?; - - match status.success() { - true => (), - false => return Err(HarmonicError::CommandFailedStatus(command_str)), - } + execute_command(&mut command).await.map_err(SetupDefaultProfileError::Command)?; } Ok(()) } @@ -123,18 +111,21 @@ impl Actionable for ActionState { } } -impl From> for ActionState { - fn from(v: ActionState) -> Self { - match v { - ActionState::Completed(_) => ActionState::Completed(Action::SetupDefaultProfile(v)), - ActionState::Planned(_) => ActionState::Planned(Action::SetupDefaultProfile(v)), - ActionState::Reverted(_) => ActionState::Reverted(Action::SetupDefaultProfile(v)), - } +impl From for Action { + fn from(v: SetupDefaultProfile) -> Self { + Action::SetupDefaultProfile(v) } } #[derive(Debug, thiserror::Error, Serialize)] pub enum SetupDefaultProfileError { - + #[error("Glob pattern error")] + GlobPatternError(#[from] #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] glob::PatternError), + #[error("Glob globbing error")] + GlobGlobError(#[from] #[source] #[serde(serialize_with = "crate::serialize_error_to_display")] glob::GlobError), + #[error("Unarchived Nix store did not appear to include a `nss-cacert` location")] + NoNssCacert, + #[error("Failed to execute command")] + Command(#[source] #[serde(serialize_with = "crate::serialize_error_to_display")] std::io::Error) } diff --git a/src/actions/base/start_systemd_unit.rs b/src/actions/base/start_systemd_unit.rs index ec51077..4837cbd 100644 --- a/src/actions/base/start_systemd_unit.rs +++ b/src/actions/base/start_systemd_unit.rs @@ -9,21 +9,22 @@ use crate::actions::{ActionDescription, Actionable, ActionState, Action, ActionE #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct StartSystemdUnit { unit: String, + action_state: ActionState, } impl StartSystemdUnit { #[tracing::instrument(skip_all)] - pub async fn plan(unit: String) -> Result, StartSystemdUnitError> { - Ok(ActionState::Planned(Self { unit })) + pub async fn plan(unit: String) -> Result { + Ok(Self { unit, action_state: ActionState::Planned }) } } #[async_trait::async_trait] -impl Actionable for ActionState { +impl Actionable for StartSystemdUnit { type Error = StartSystemdUnitError; fn description(&self) -> Vec { - match self { - ActionState::Planned(v) => vec![ + match self.action_state { + ActionState::Planned => vec![ ActionDescription::new( "Start the systemd Nix service and socket".to_string(), vec![ @@ -31,7 +32,7 @@ impl Actionable for ActionState { ] ), ], - ActionState::Completed(_) => vec![ + ActionState::Completed => vec![ ActionDescription::new( "Stop the systemd Nix service and socket".to_string(), vec![ @@ -39,7 +40,7 @@ impl Actionable for ActionState { ] ), ], - ActionState::Reverted(_) => vec![ + ActionState::Reverted => vec![ ActionDescription::new( "Stopped the systemd Nix service and socket".to_string(), vec![ @@ -51,12 +52,9 @@ impl Actionable for ActionState { } #[tracing::instrument(skip_all)] - async fn execute(&mut self) -> Result<(), ActionError> { - let StartSystemdUnit { unit } = match self { - ActionState::Completed(_) => return Err(ActionError::AlreadyExecuted(self.clone().into())), - ActionState::Reverted(_) => return Err(ActionError::AlreadyReverted(self.clone().into())), - ActionState::Planned(v) => v, - }; + async fn execute(&mut self) -> Result<(), Self::Error> { + let Self { unit, action_state } = self; + // TODO(@Hoverbear): Handle proxy vars execute_command( Command::new("systemctl") @@ -66,30 +64,26 @@ impl Actionable for ActionState { ) .await.map_err(StartSystemdUnitError::Command)?; + *action_state = ActionState::Completed; Ok(()) } #[tracing::instrument(skip_all)] - async fn revert(&mut self) -> Result<(), ActionError> { + async fn revert(&mut self) -> Result<(), Self::Error> { todo!(); Ok(()) } } -impl From> for ActionState { - fn from(v: ActionState) -> Self { - match v { - ActionState::Completed(_) => ActionState::Completed(Action::StartSystemdUnit(v)), - ActionState::Planned(_) => ActionState::Planned(Action::StartSystemdUnit(v)), - ActionState::Reverted(_) => ActionState::Reverted(Action::StartSystemdUnit(v)), - } +impl From for Action { + fn from(v: StartSystemdUnit) -> Self { + Action::StartSystemdUnit(v) } } #[derive(Debug, thiserror::Error, Serialize)] pub enum StartSystemdUnitError { #[error("Failed to execute command")] - #[serde(serialize_with = "crate::serialize_std_io_error_to_display")] - Command(#[source] std::io::Error) + Command(#[source] #[serde(serialize_with = "crate::serialize_error_to_display")] std::io::Error) } diff --git a/src/actions/meta/configure_nix.rs b/src/actions/meta/configure_nix.rs index 795d2d4..a879a81 100644 --- a/src/actions/meta/configure_nix.rs +++ b/src/actions/meta/configure_nix.rs @@ -5,7 +5,7 @@ use crate::actions::{ ConfigureNixDaemonService, ConfigureNixDaemonServiceError, PlaceNixConfiguration, PlaceNixConfigurationError, SetupDefaultProfile, SetupDefaultProfileError, PlaceChannelConfiguration, PlaceChannelConfigurationError, }, - meta::{ConfigureShellProfile, ConfigureShellProfileError}, ActionState, Action, + meta::{ConfigureShellProfile, ConfigureShellProfileError}, ActionState, Action, ActionError, }; use crate::{HarmonicError, InstallSettings}; @@ -18,11 +18,12 @@ pub struct ConfigureNix { place_channel_configuration: PlaceChannelConfiguration, place_nix_configuration: PlaceNixConfiguration, configure_nix_daemon_service: ConfigureNixDaemonService, + action_state: ActionState, } impl ConfigureNix { #[tracing::instrument(skip_all)] - pub async fn plan(settings: InstallSettings) -> Result, ConfigureNixError> { + pub async fn plan(settings: InstallSettings) -> Result { let channels = settings .channels .iter() @@ -48,12 +49,13 @@ impl ConfigureNix { setup_default_profile, configure_nix_daemon_service, configure_shell_profile, + action_state: ActionState::Planned, }) } } #[async_trait::async_trait] -impl Actionable for ActionState { +impl Actionable for ConfigureNix { type Error = ConfigureNixError; fn description(&self) -> Vec { let Self { @@ -62,6 +64,7 @@ impl Actionable for ActionState { place_nix_configuration, place_channel_configuration, configure_shell_profile, + action_state: _, } = &self; let mut buf = setup_default_profile.description(); @@ -83,6 +86,7 @@ impl Actionable for ActionState { place_nix_configuration, place_channel_configuration, configure_shell_profile, + action_state, } = self; let ( @@ -92,29 +96,24 @@ impl Actionable for ActionState { configure_shell_profile, ) = if let Some(configure_shell_profile) = configure_shell_profile { let (a, b, c, d) = tokio::try_join!( - setup_default_profile.execute(), - place_nix_configuration.execute(), - place_channel_configuration.execute(), - configure_shell_profile.execute(), + async move { setup_default_profile.execute().await.map_err(|e| ConfigureNixError::from(e)) }, + async move { place_nix_configuration.execute().await.map_err(|e| ConfigureNixError::from(e)) }, + async move { place_channel_configuration.execute().await.map_err(|e| ConfigureNixError::from(e)) }, + async move { configure_shell_profile.execute().await.map_err(|e| ConfigureNixError::from(e)) }, )?; (a, b, c, Some(d)) } else { let (a, b, c) = tokio::try_join!( - setup_default_profile.execute(), - place_nix_configuration.execute(), - place_channel_configuration.execute(), + async move { setup_default_profile.execute().await.map_err(|e| ConfigureNixError::from(e)) }, + async move { place_nix_configuration.execute().await.map_err(|e| ConfigureNixError::from(e)) }, + async move { place_channel_configuration.execute().await.map_err(|e| ConfigureNixError::from(e)) }, )?; (a, b, c, None) }; let configure_nix_daemon_service = configure_nix_daemon_service.execute().await?; - Ok(Self::Receipt { - setup_default_profile, - configure_nix_daemon_service, - place_nix_configuration, - place_channel_configuration, - configure_shell_profile, - }) + *action_state = ActionState::Completed; + Ok(()) } @@ -126,17 +125,22 @@ impl Actionable for ActionState { } } -impl From> for ActionState { - fn from(v: ActionState) -> Self { - match v { - ActionState::Completed(_) => ActionState::Completed(Action::ConfigureNix(v)), - ActionState::Planned(_) => ActionState::Planned(Action::ConfigureNix(v)), - ActionState::Reverted(_) => ActionState::Reverted(Action::ConfigureNix(v)), - } +impl From for Action { + fn from(v: ConfigureNix) -> Self { + Action::ConfigureNix(v) } } #[derive(Debug, thiserror::Error, Serialize)] pub enum ConfigureNixError { - + #[error(transparent)] + SetupDefaultProfile(#[from] SetupDefaultProfileError), + #[error(transparent)] + PlaceNixConfiguration(#[from] PlaceNixConfigurationError), + #[error(transparent)] + PlaceChannelConfiguration(#[from] PlaceChannelConfigurationError), + #[error(transparent)] + ConfigureNixDaemonService(#[from] ConfigureNixDaemonServiceError), + #[error(transparent)] + ConfigureShellProfile(#[from] ConfigureShellProfileError), } \ No newline at end of file diff --git a/src/actions/meta/configure_shell_profile.rs b/src/actions/meta/configure_shell_profile.rs index f38f5ae..0901adf 100644 --- a/src/actions/meta/configure_shell_profile.rs +++ b/src/actions/meta/configure_shell_profile.rs @@ -1,12 +1,12 @@ use std::path::Path; use serde::Serialize; -use tokio::task::JoinSet; +use tokio::task::{JoinSet, JoinError}; use crate::HarmonicError; use crate::actions::base::{CreateOrAppendFile, CreateOrAppendFileError}; -use crate::actions::{ActionDescription, Actionable, ActionState, Action}; +use crate::actions::{ActionDescription, Actionable, ActionState, Action, ActionError}; const PROFILE_TARGETS: &[&str] = &[ "/etc/bashrc", @@ -21,11 +21,12 @@ const PROFILE_NIX_FILE: &str = "/nix/var/nix/profiles/default/etc/profile.d/nix- #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct ConfigureShellProfile { create_or_append_files: Vec, + action_state: ActionState, } impl ConfigureShellProfile { #[tracing::instrument(skip_all)] - pub async fn plan() -> Result, ConfigureShellProfileError> { + pub async fn plan() -> Result { let mut create_or_append_files = Vec::default(); for profile_target in PROFILE_TARGETS { let path = Path::new(profile_target); @@ -50,12 +51,13 @@ impl ConfigureShellProfile { Ok(Self { create_or_append_files, + action_state: ActionState::Planned, }) } } #[async_trait::async_trait] -impl Actionable for ActionState { +impl Actionable for ConfigureShellProfile { type Error = ConfigureShellProfileError; fn description(&self) -> Vec { vec![ActionDescription::new( @@ -68,34 +70,35 @@ impl Actionable for ActionState { async fn execute(&mut self) -> Result<(), Self::Error> { let Self { create_or_append_files, + action_state, } = self; tracing::info!("Configuring shell profile"); let mut set = JoinSet::new(); - - let mut successes = Vec::with_capacity(create_or_append_files.len()); let mut errors = Vec::default(); - for create_or_append_file in create_or_append_files { - let _abort_handle = set.spawn(async move { create_or_append_file.execute().await }); + for (idx, create_or_append_file) in create_or_append_files.iter().enumerate() { + let mut create_or_append_file_clone = create_or_append_file.clone(); + let _abort_handle = set.spawn(async move { create_or_append_file_clone.execute().await?; Result::<_, CreateOrAppendFileError>::Ok((idx, create_or_append_file_clone)) }); } while let Some(result) = set.join_next().await { match result { - Ok(Ok(())) => (), + Ok(Ok((idx, create_or_append_file))) => create_or_append_files[idx] = create_or_append_file, Ok(Err(e)) => errors.push(e), - Err(e) => errors.push(e.into()), + Err(e) => return Err(e.into()), }; } if !errors.is_empty() { if errors.len() == 1 { - return Err(errors.into_iter().next().unwrap()); + return Err(errors.into_iter().next().unwrap().into()); } else { - return Err(HarmonicError::Multiple(errors)); + return Err(ConfigureShellProfileError::MultipleCreateOrAppendFile(errors)); } } + *action_state = ActionState::Completed; Ok(()) } @@ -108,17 +111,18 @@ impl Actionable for ActionState { } } -impl From> for ActionState { - fn from(v: ActionState) -> Self { - match v { - ActionState::Completed(_) => ActionState::Completed(Action::ConfigureShellProfile(v)), - ActionState::Planned(_) => ActionState::Planned(Action::ConfigureShellProfile(v)), - ActionState::Reverted(_) => ActionState::Reverted(Action::ConfigureShellProfile(v)), - } +impl From for Action { + fn from(v: ConfigureShellProfile) -> Self { + Action::ConfigureShellProfile(v) } } #[derive(Debug, thiserror::Error, Serialize)] pub enum ConfigureShellProfileError { - + #[error(transparent)] + CreateOrAppendFile(#[from] CreateOrAppendFileError), + #[error("Multiple errors: {}", .0.iter().map(|v| format!("{v}")).collect::>().join(" & "))] + MultipleCreateOrAppendFile(Vec), + #[error(transparent)] + Join(#[from] #[serde(serialize_with = "crate::serialize_error_to_display")] JoinError), } diff --git a/src/actions/meta/create_nix_tree.rs b/src/actions/meta/create_nix_tree.rs index e0b51e3..4d222b7 100644 --- a/src/actions/meta/create_nix_tree.rs +++ b/src/actions/meta/create_nix_tree.rs @@ -3,7 +3,7 @@ use serde::Serialize; use crate::HarmonicError; use crate::actions::base::{CreateDirectory, CreateDirectoryError}; -use crate::actions::{ActionDescription, Actionable, ActionState, Action}; +use crate::actions::{ActionDescription, Actionable, ActionState, Action, ActionError}; const PATHS: &[&str] = &[ "/nix", @@ -25,11 +25,12 @@ const PATHS: &[&str] = &[ #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct CreateNixTree { create_directories: Vec, + action_state: ActionState, } impl CreateNixTree { #[tracing::instrument(skip_all)] - pub async fn plan(force: bool) -> Result { + pub async fn plan(force: bool) -> Result { let mut create_directories = Vec::default(); for path in PATHS { // We use `create_dir` over `create_dir_all` to ensure we always set permissions right @@ -38,12 +39,12 @@ impl CreateNixTree { ) } - Ok(Self { create_directories }) + Ok(Self { create_directories, action_state: ActionState::Planned }) } } #[async_trait::async_trait] -impl Actionable for ActionState { +impl Actionable for CreateNixTree { type Error = CreateNixTreeError; fn description(&self) -> Vec { vec![ActionDescription::new( @@ -56,15 +57,15 @@ impl Actionable for ActionState { } #[tracing::instrument(skip_all)] - async fn execute(&mut self) -> Result<(), HarmonicError> { - let Self { create_directories } = self; + async fn execute(&mut self) -> Result<(), Self::Error> { + let Self { create_directories, action_state } = self; - let mut successes = Vec::with_capacity(create_directories.len()); // Just do sequential since parallizing this will have little benefit for create_directory in create_directories { create_directory.execute().await? } + *action_state = ActionState::Completed; Ok(()) } @@ -77,17 +78,14 @@ impl Actionable for ActionState { } } -impl From> for ActionState { - fn from(v: ActionState) -> Self { - match v { - ActionState::Completed(_) => ActionState::Completed(Action::CreateNixTree(v)), - ActionState::Planned(_) => ActionState::Planned(Action::CreateNixTree(v)), - ActionState::Reverted(_) => ActionState::Reverted(Action::CreateNixTree(v)), - } +impl From for Action { + fn from(v: CreateNixTree) -> Self { + Action::CreateNixTree(v) } } #[derive(Debug, thiserror::Error, Serialize)] pub enum CreateNixTreeError { - + #[error(transparent)] + CreateDirectory(#[from] CreateDirectoryError), } diff --git a/src/actions/meta/create_users_and_group.rs b/src/actions/meta/create_users_and_group.rs index 79210be..84d23f6 100644 --- a/src/actions/meta/create_users_and_group.rs +++ b/src/actions/meta/create_users_and_group.rs @@ -1,5 +1,5 @@ use serde::Serialize; -use tokio::task::JoinSet; +use tokio::task::{JoinSet, JoinError}; use crate::{HarmonicError, InstallSettings}; @@ -15,11 +15,12 @@ pub struct CreateUsersAndGroup { nix_build_user_id_base: usize, create_group: CreateGroup, create_users: Vec, + action_state: ActionState, } impl CreateUsersAndGroup { #[tracing::instrument(skip_all)] - pub async fn plan(settings: InstallSettings) -> Result { + pub async fn plan(settings: InstallSettings) -> Result { // TODO(@hoverbear): CHeck if it exist, error if so let create_group = CreateGroup::plan( settings.nix_build_group_name.clone(), @@ -43,12 +44,13 @@ impl CreateUsersAndGroup { nix_build_user_id_base: settings.nix_build_user_id_base, create_group, create_users, + action_state: ActionState::Planned, }) } } #[async_trait::async_trait] -impl Actionable for ActionState { +impl Actionable for CreateUsersAndGroup { type Error = CreateUsersAndGroupError; fn description(&self) -> Vec { let Self { @@ -57,7 +59,9 @@ impl Actionable for ActionState { nix_build_group_id, nix_build_user_prefix, nix_build_user_id_base, - .. + create_group: _, + create_users: _, + action_state: _, } = &self; vec![ @@ -73,13 +77,19 @@ impl Actionable for ActionState { } #[tracing::instrument(skip_all)] - async fn execute(&mut self) -> Result<(), HarmonicError> { + async fn execute(&mut self) -> Result<(), Self::Error> { let Self { create_users, - create_group, - .. + create_group, + daemon_user_count, + nix_build_group_name, + nix_build_group_id, + nix_build_user_prefix, + nix_build_user_id_base, + action_state } = self; + // Create group let create_group = create_group.execute().await?; @@ -87,33 +97,32 @@ impl Actionable for ActionState { // TODO(@hoverbear): Abstract this, it will be common let mut set = JoinSet::new(); - let mut successes = Vec::with_capacity(create_users.len()); let mut errors = Vec::default(); - for create_user in create_users { - let _abort_handle = set.spawn(async move { create_user.execute().await }); + for (idx, create_user) in create_users.iter().enumerate() { + let mut create_user_clone = create_user.clone(); + let _abort_handle = set.spawn(async move { create_user_clone.execute().await?; Result::<_, CreateUserError>::Ok((idx, create_user_clone)) }); } while let Some(result) = set.join_next().await { match result { - Ok(Ok(success)) => successes.push(success), + Ok(Ok((idx, success))) => create_users[idx] = success, Ok(Err(e)) => errors.push(e), - Err(e) => errors.push(e.into()), + Err(e) => return Err(e)?, }; } if !errors.is_empty() { if errors.len() == 1 { - return Err(errors.into_iter().next().unwrap()); + return Err(errors.into_iter().next().unwrap().into()); } else { - return Err(HarmonicError::Multiple(errors)); + return Err(CreateUsersAndGroupError::CreateUsers(errors)); } } - Ok(Self::Receipt { - create_group, - create_users: successes, - }) + + *action_state = ActionState::Completed; + Ok(()) } #[tracing::instrument(skip_all)] @@ -125,19 +134,26 @@ impl Actionable for ActionState { } -impl From> for ActionState { - fn from(v: ActionState) -> Self { - match v { - ActionState::Completed(_) => ActionState::Completed(Action::CreateUsersAndGroup(v)), - ActionState::Planned(_) => ActionState::Planned(Action::CreateUsersAndGroup(v)), - ActionState::Reverted(_) => ActionState::Reverted(Action::CreateUsersAndGroup(v)), - } +impl From for Action { + fn from(v: CreateUsersAndGroup) -> Self { + Action::CreateUsersAndGroup(v) } } #[derive(Debug, thiserror::Error, Serialize)] pub enum CreateUsersAndGroupError { - + #[error(transparent)] + CreateUser(#[from] CreateUserError), + #[error("Multiple errors: {}", .0.iter().map(|v| format!("{v}")).collect::>().join(" & "))] + CreateUsers(Vec), + #[error(transparent)] + CreateGroup(#[from] CreateGroupError), + #[error(transparent)] + Join( + #[from] + #[serde(serialize_with = "crate::serialize_error_to_display")] + JoinError + ), } diff --git a/src/actions/meta/provision_nix.rs b/src/actions/meta/provision_nix.rs index 0418960..6433bdd 100644 --- a/src/actions/meta/provision_nix.rs +++ b/src/actions/meta/provision_nix.rs @@ -1,10 +1,11 @@ use serde::Serialize; use tempdir::TempDir; +use tokio::task::JoinError; use crate::actions::base::{FetchNix, FetchNixError, MoveUnpackedNix, MoveUnpackedNixError}; use crate::{HarmonicError, InstallSettings}; -use crate::actions::{ActionDescription, Actionable, ActionState, Action}; +use crate::actions::{ActionDescription, Actionable, ActionState, Action, ActionError}; use super::{ CreateNixTree, CreateNixTreeError, @@ -17,11 +18,12 @@ pub struct ProvisionNix { create_users_and_group: CreateUsersAndGroup, create_nix_tree: CreateNixTree, move_unpacked_nix: MoveUnpackedNix, + action_state: ActionState, } impl ProvisionNix { #[tracing::instrument(skip_all)] - pub async fn plan(settings: InstallSettings) -> Result, ProvisionNixError> { + pub async fn plan(settings: InstallSettings) -> Result { let tempdir = TempDir::new("nix").map_err(ProvisionNixError::TempDir)?; let fetch_nix = FetchNix::plan( @@ -32,29 +34,26 @@ impl ProvisionNix { let create_users_and_group = CreateUsersAndGroup::plan(settings.clone()).await?; let create_nix_tree = CreateNixTree::plan(settings.force).await?; let move_unpacked_nix = MoveUnpackedNix::plan(tempdir.path().to_path_buf()).await?; - Ok(ActionState::Planned(Self { + Ok(Self { fetch_nix, create_users_and_group, create_nix_tree, move_unpacked_nix, - })) + action_state: ActionState::Planned, + }) } } #[async_trait::async_trait] -impl Actionable for ActionState { +impl Actionable for ProvisionNix { type Error = ProvisionNixError; fn description(&self) -> Vec { - match self { - ActionState::Completed(action) => action.start_systemd_socket.description(), - ActionState::Planned(action) => action.start_systemd_socket.description(), - ActionState::Reverted(_) => todo!(), - } let Self { fetch_nix, create_users_and_group, create_nix_tree, move_unpacked_nix, + action_state: _, } = &self; let mut buf = fetch_nix.description(); @@ -72,17 +71,20 @@ impl Actionable for ActionState { create_nix_tree, create_users_and_group, move_unpacked_nix, + action_state, } = self; // We fetch nix while doing the rest, then move it over. - let fetch_nix_handle = tokio::spawn(async move { fetch_nix.execute().await }); + let mut fetch_nix_clone = fetch_nix.clone(); + let fetch_nix_handle = tokio::task::spawn_local(async { fetch_nix_clone.execute().await?; Result::<_, Self::Error>::Ok(fetch_nix_clone) }); create_users_and_group.execute().await?; - create_nix_tree.execute().await?; + create_nix_tree.execute().await.map_err(ProvisionNixError::from)?; - fetch_nix_handle.await??; + *fetch_nix = fetch_nix_handle.await.map_err(ProvisionNixError::from)??; move_unpacked_nix.execute().await?; + *action_state = ActionState::Completed; Ok(()) } @@ -94,13 +96,9 @@ impl Actionable for ActionState { } } -impl From> for ActionState { - fn from(v: ActionState) -> Self { - match v { - ActionState::Completed(_) => ActionState::Completed(Action::ProvisionNix(v)), - ActionState::Planned(_) => ActionState::Planned(Action::ProvisionNix(v)), - ActionState::Reverted(_) => ActionState::Reverted(Action::ProvisionNix(v)), - } +impl From for Action { + fn from(v: ProvisionNix) -> Self { + Action::ProvisionNix(v) } } @@ -108,6 +106,23 @@ impl From> for ActionState { #[derive(Debug, thiserror::Error, Serialize)] pub enum ProvisionNixError { #[error("Failed create tempdir")] - #[serde(serialize_with = "crate::serialize_std_io_error_to_display")] - TempDir(#[source] std::io::Error) + TempDir( + #[source] + #[serde(serialize_with = "crate::serialize_error_to_display")] + std::io::Error + ), + #[error(transparent)] + FetchNix(#[from] FetchNixError), + #[error(transparent)] + Join( + #[from] + #[serde(serialize_with = "crate::serialize_error_to_display")] + JoinError + ), + #[error(transparent)] + CreateUsersAndGroup(#[from] CreateUsersAndGroupError), + #[error(transparent)] + CreateNixTree(#[from] CreateNixTreeError), + #[error(transparent)] + MoveUnpackedNix(#[from] MoveUnpackedNixError), } diff --git a/src/actions/meta/start_nix_daemon.rs b/src/actions/meta/start_nix_daemon.rs index ed47c36..540255b 100644 --- a/src/actions/meta/start_nix_daemon.rs +++ b/src/actions/meta/start_nix_daemon.rs @@ -8,68 +8,52 @@ use crate::actions::{ActionDescription, Actionable, ActionState, Action, ActionE /// This is mostly indirection for supporting non-systemd #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct StartNixDaemon { - start_systemd_socket: ActionState, + start_systemd_socket: StartSystemdUnit, + action_state: ActionState, } impl StartNixDaemon { #[tracing::instrument(skip_all)] - pub async fn plan() -> Result, StartNixDaemonError> { + pub async fn plan() -> Result { let start_systemd_socket = StartSystemdUnit::plan("nix-daemon.socket".into()).await?; - Ok(ActionState::Planned(Self { + Ok(Self { start_systemd_socket, - })) + action_state: ActionState::Planned, + }) } } #[async_trait::async_trait] -impl Actionable for ActionState { +impl Actionable for StartNixDaemon { type Error = StartNixDaemonError; fn description(&self) -> Vec { - let StartNixDaemon { start_systemd_socket } = match self { - ActionState::Completed(v) | ActionState::Reverted(v) | ActionState::Planned(v) => v, - }; - start_systemd_socket.description() + self.start_systemd_socket.description() } #[tracing::instrument(skip_all)] - async fn execute(self) -> Result { - let StartNixDaemon { start_systemd_socket } = match self { - ActionState::Planned(v) => v, - ActionState::Completed(_) => return Err(ActionError::AlreadyExecuted(self.clone().into())), - ActionState::Reverted(_) => return Err(ActionError::AlreadyReverted(self.clone().into())), - }; + async fn execute(&mut self) -> Result<(), Self::Error> { + let Self { start_systemd_socket, action_state } = self; start_systemd_socket.execute().await?; - Ok(Self::Completed(StartNixDaemon { - start_systemd_socket, - })) + Ok(()) } #[tracing::instrument(skip_all)] - async fn revert(self) -> Result { - let StartNixDaemon { start_systemd_socket } = match self { - ActionState::Planned(v) => return Err(ActionError::NotExecuted(self.clone().into())), - ActionState::Completed(v) => v, - ActionState::Reverted(_) => return Err(ActionError::AlreadyReverted(self.clone().into())), - }; + async fn revert(&mut self) -> Result<(), Self::Error> { + let Self { start_systemd_socket, action_state, .. } = self; start_systemd_socket.revert().await?; - Ok(Self::Reverted(StartNixDaemon { - start_systemd_socket, - })) + *action_state = ActionState::Completed; + Ok(()) } } -impl From> for ActionState { - fn from(v: ActionState) -> Self { - match v { - ActionState::Completed(_) => ActionState::Completed(Action::StartNixDaemon(v)), - ActionState::Planned(_) => ActionState::Planned(Action::StartNixDaemon(v)), - ActionState::Reverted(_) => ActionState::Reverted(Action::StartNixDaemon(v)), - } +impl From for Action { + fn from(v: StartNixDaemon) -> Self { + Action::StartNixDaemon(v) } } diff --git a/src/actions/mod.rs b/src/actions/mod.rs index 3739273..a7a2491 100644 --- a/src/actions/mod.rs +++ b/src/actions/mod.rs @@ -22,24 +22,21 @@ use serde::{Deserialize, de::DeserializeOwned, Serialize}; use self::base::{StartSystemdUnit, StartSystemdUnitError}; #[async_trait::async_trait] -pub trait Actionable: DeserializeOwned + Serialize + Into> { - type Error: std::error::Error + std::fmt::Debug + Serialize; +pub trait Actionable: DeserializeOwned + Serialize + Into { + type Error: std::error::Error + std::fmt::Debug + Serialize + Into; fn description(&self) -> Vec; // They should also have an `async fn plan(args...) -> Result, Self::Error>;` - async fn execute(self) -> Result; - async fn revert(self) -> Result; + async fn execute(&mut self) -> Result<(), Self::Error>; + async fn revert(&mut self) -> Result<(), Self::Error>; } -#[derive(thiserror::Error, Debug, Serialize, Deserialize, Clone)] -pub enum ActionState

where P: Serialize + DeserializeOwned + Clone { - #[serde(bound = "P: DeserializeOwned")] - Completed(P), - #[serde(bound = "P: DeserializeOwned")] - Planned(P), - #[serde(bound = "P: DeserializeOwned")] - Reverted(P), +#[derive(Debug, Serialize, Deserialize, Clone)] +pub enum ActionState { + Completed, + Planned, + Reverted, } #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] @@ -60,34 +57,34 @@ impl ActionDescription { #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub enum Action { - ConfigureNixDaemonService(ActionState), - ConfigureNix(ActionState), - ConfigureShellProfile(ActionState), - CreateDirectory(ActionState), - CreateFile(ActionState), - CreateGroup(ActionState), - CreateOrAppendFile(ActionState), - CreateNixTree(ActionState), - CreateUser(ActionState), - CreateUsersAndGroup(ActionState), - FetchNix(ActionState), - MoveUnpackedNix(ActionState), - PlaceChannelConfiguration(ActionState), - PlaceNixConfiguration(ActionState), - SetupDefaultProfile(ActionState), - StartNixDaemon(ActionState), - StartSystemdUnit(ActionState), - ProvisionNix(ActionState), + ConfigureNixDaemonService(ConfigureNixDaemonService), + ConfigureNix(ConfigureNix), + ConfigureShellProfile(ConfigureShellProfile), + CreateDirectory(CreateDirectory), + CreateFile(CreateFile), + CreateGroup(CreateGroup), + CreateOrAppendFile(CreateOrAppendFile), + CreateNixTree(CreateNixTree), + CreateUser(CreateUser), + CreateUsersAndGroup(CreateUsersAndGroup), + FetchNix(FetchNix), + MoveUnpackedNix(MoveUnpackedNix), + PlaceChannelConfiguration(PlaceChannelConfiguration), + PlaceNixConfiguration(PlaceNixConfiguration), + SetupDefaultProfile(SetupDefaultProfile), + StartNixDaemon(StartNixDaemon), + StartSystemdUnit(StartSystemdUnit), + ProvisionNix(ProvisionNix), } #[derive(Debug, thiserror::Error, serde::Serialize)] pub enum ActionError { #[error("Attempted to revert an unexecuted action")] - NotExecuted(ActionState), + NotExecuted(Action), #[error("Attempted to execute an already executed action")] - AlreadyExecuted(ActionState), + AlreadyExecuted(Action), #[error("Attempted to revert an already reverted action")] - AlreadyReverted(ActionState), + AlreadyReverted(Action), #[error(transparent)] ConfigureNixDaemonService(#[from] ConfigureNixDaemonServiceError), #[error(transparent)] @@ -127,13 +124,10 @@ pub enum ActionError { } #[async_trait::async_trait] -impl Actionable for ActionState { +impl Actionable for Action { type Error = ActionError; fn description(&self) -> Vec { - let inner = match self { - ActionState::Planned(p) | ActionState::Completed(p) | ActionState::Reverted(p) => p, - }; - match inner { + match self { Action::ConfigureNixDaemonService(i) => i.description(), Action::ConfigureNix(i) => i.description(), Action::ConfigureShellProfile(i) => i.description(), @@ -156,58 +150,50 @@ impl Actionable for ActionState { } async fn execute(&mut self) -> Result<(), Self::Error> { - let inner = match self { - ActionState::Completed(p) => todo!(), - ActionState::Planned(p) => p, - ActionState::Reverted(p) => todo!(), + match self { + Action::ConfigureNixDaemonService(i) => i.execute().await?, + Action::ConfigureNix(i) => i.execute().await?, + Action::ConfigureShellProfile(i) => i.execute().await?, + Action::CreateDirectory(i) => i.execute().await?, + Action::CreateFile(i) => i.execute().await?, + Action::CreateGroup(i) => i.execute().await?, + Action::CreateOrAppendFile(i) => i.execute().await?, + Action::CreateNixTree(i) => i.execute().await?, + Action::CreateUser(i) => i.execute().await?, + Action::CreateUsersAndGroup(i) => i.execute().await?, + Action::FetchNix(i) => i.execute().await?, + Action::MoveUnpackedNix(i) => i.execute().await?, + Action::PlaceChannelConfiguration(i) => i.execute().await?, + Action::PlaceNixConfiguration(i) => i.execute().await?, + Action::SetupDefaultProfile(i) => i.execute().await?, + Action::StartNixDaemon(i) => i.execute().await?, + Action::StartSystemdUnit(i) => i.execute().await?, + Action::ProvisionNix(i) => i.execute().await?, }; - match inner { - Action::ConfigureNixDaemonService(i) => i.execute().await, - Action::ConfigureNix(i) => i.execute().await, - Action::ConfigureShellProfile(i) => i.execute().await, - Action::CreateDirectory(i) => i.execute().await, - Action::CreateFile(i) => i.execute().await, - Action::CreateGroup(i) => i.execute().await, - Action::CreateOrAppendFile(i) => i.execute().await, - Action::CreateNixTree(i) => i.execute().await, - Action::CreateUser(i) => i.execute().await, - Action::CreateUsersAndGroup(i) => i.execute().await, - Action::FetchNix(i) => i.execute().await, - Action::MoveUnpackedNix(i) => i.execute().await, - Action::PlaceChannelConfiguration(i) => i.execute().await, - Action::PlaceNixConfiguration(i) => i.execute().await, - Action::SetupDefaultProfile(i) => i.execute().await, - Action::StartNixDaemon(i) => i.execute().await, - Action::StartSystemdUnit(i) => i.execute().await, - Action::ProvisionNix(i) => i.execute().await, - } + Ok(()) } async fn revert(&mut self) -> Result<(), Self::Error> { - let inner = match self { - ActionState::Planned(p) => todo!(), - ActionState::Completed(p) => p, - ActionState::Reverted(p) => todo!(), - }; - match inner { - Action::ConfigureNixDaemonService(i) => i.revert().await, - Action::ConfigureNix(i) => i.revert().await, - Action::ConfigureShellProfile(i) => i.revert().await, - Action::CreateDirectory(i) => i.revert().await, - Action::CreateFile(i) => i.revert().await, - Action::CreateGroup(i) => i.revert().await, - Action::CreateOrAppendFile(i) => i.revert().await, - Action::CreateNixTree(i) => i.revert().await, - Action::CreateUser(i) => i.revert().await, - Action::CreateUsersAndGroup(i) => i.revert().await, - Action::FetchNix(i) => i.revert().await, - Action::MoveUnpackedNix(i) => i.revert().await, - Action::PlaceChannelConfiguration(i) => i.revert().await, - Action::PlaceNixConfiguration(i) => i.revert().await, - Action::SetupDefaultProfile(i) => i.revert().await, - Action::StartNixDaemon(i) => i.revert().await, - Action::StartSystemdUnit(i) => i.revert().await, - Action::ProvisionNix(i) => i.revert().await, + match self { + Action::ConfigureNixDaemonService(i) => i.revert().await?, + Action::ConfigureNix(i) => i.revert().await?, + Action::ConfigureShellProfile(i) => i.revert().await?, + Action::CreateDirectory(i) => i.revert().await?, + Action::CreateFile(i) => i.revert().await?, + Action::CreateGroup(i) => i.revert().await?, + Action::CreateOrAppendFile(i) => i.revert().await?, + Action::CreateNixTree(i) => i.revert().await?, + Action::CreateUser(i) => i.revert().await?, + Action::CreateUsersAndGroup(i) => i.revert().await?, + Action::FetchNix(i) => i.revert().await?, + Action::MoveUnpackedNix(i) => i.revert().await?, + Action::PlaceChannelConfiguration(i) => i.revert().await?, + Action::PlaceNixConfiguration(i) => i.revert().await?, + Action::SetupDefaultProfile(i) => i.revert().await?, + Action::StartNixDaemon(i) => i.revert().await?, + Action::StartSystemdUnit(i) => i.revert().await?, + Action::ProvisionNix(i) => i.revert().await?, } + Ok(()) } } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 3003381..e71a0af 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -3,7 +3,7 @@ pub(crate) mod subcommand; use crate::{cli::arg::ChannelValue, interaction}; use clap::{ArgAction, Parser}; -use harmonic::{Harmonic, InstallPlan, InstallSettings}; +use harmonic::{InstallPlan, InstallSettings}; use std::process::ExitCode; use self::subcommand::HarmonicSubcommand; @@ -92,7 +92,7 @@ impl CommandExecute for HarmonicCli { ); settings.modify_profile(!no_modify_profile); - let plan = InstallPlan::new(settings).await?; + let mut plan = InstallPlan::new(settings).await?; // TODO(@Hoverbear): Make this smarter if !interaction::confirm(plan.description()).await? { diff --git a/src/lib.rs b/src/lib.rs index d437152..56f75cb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,7 @@ use std::{ io::SeekFrom, os::unix::fs::PermissionsExt, path::{Path, PathBuf}, - process::ExitStatus, + process::ExitStatus, fmt::Display, }; pub use error::HarmonicError; @@ -280,6 +280,6 @@ fn set_env(k: impl AsRef, v: impl AsRef, dry_run: bool) { } } -fn serialize_std_io_error_to_display(err: &std::io::Error, ser: S) -> Result where S: Serializer { +fn serialize_error_to_display(err: &E, ser: S) -> Result where E: Display, S: Serializer { ser.serialize_str(&format!("{err:#}")) } \ No newline at end of file diff --git a/src/plan.rs b/src/plan.rs index 284fea0..9e8edbb 100644 --- a/src/plan.rs +++ b/src/plan.rs @@ -30,9 +30,9 @@ pub struct InstallPlan { * "Start Nix" * start_nix_daemon_service */ - provision_nix: ActionState, - configure_nix: ActionState, - start_nix_daemon: ActionState, + provision_nix: ProvisionNix, + configure_nix: ConfigureNix, + start_nix_daemon: StartNixDaemon, } impl InstallPlan {