diff --git a/src/actions/base/configure_nix_daemon_service.rs b/src/actions/base/configure_nix_daemon_service.rs index 31e750c..cb4e284 100644 --- a/src/actions/base/configure_nix_daemon_service.rs +++ b/src/actions/base/configure_nix_daemon_service.rs @@ -7,7 +7,7 @@ use tokio::process::Command; use crate::execute_command; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; 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"; @@ -21,7 +21,7 @@ pub struct ConfigureNixDaemonService { impl ConfigureNixDaemonService { #[tracing::instrument(skip_all)] - pub async fn plan() -> Result { + pub async fn plan() -> Result> { match OperatingSystem::host() { OperatingSystem::MacOSX { major: _, @@ -31,7 +31,7 @@ impl ConfigureNixDaemonService { | OperatingSystem::Darwin => (), _ => { if !Path::new("/run/systemd/system").exists() { - return Err(ConfigureNixDaemonServiceError::InitNotSupported); + return Err(ConfigureNixDaemonServiceError::InitNotSupported.boxed()); } }, }; @@ -43,9 +43,8 @@ impl ConfigureNixDaemonService { } #[async_trait::async_trait] +#[typetag::serde(name = "configure-nix-daemon")] impl Actionable for ConfigureNixDaemonService { - type Error = ConfigureNixDaemonServiceError; - fn describe_execute(&self) -> Vec { if self.action_state == ActionState::Completed { vec![] @@ -63,7 +62,7 @@ impl Actionable for ConfigureNixDaemonService { } #[tracing::instrument(skip_all)] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { action_state } = self; if *action_state == ActionState::Completed { tracing::trace!("Already completed: Configuring nix daemon service"); @@ -85,11 +84,12 @@ impl Actionable for ConfigureNixDaemonService { tokio::fs::copy(src.clone(), DARWIN_NIX_DAEMON_DEST) .await .map_err(|e| { - Self::Error::Copy( + ConfigureNixDaemonServiceError::Copy( src.to_path_buf(), PathBuf::from(DARWIN_NIX_DAEMON_DEST), e, ) + .boxed() })?; execute_command( @@ -98,18 +98,19 @@ impl Actionable for ConfigureNixDaemonService { .arg(DARWIN_NIX_DAEMON_DEST), ) .await - .map_err(Self::Error::CommandFailed)?; + .map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?; }, _ => { tracing::trace!(src = TMPFILES_SRC, dest = TMPFILES_DEST, "Symlinking"); tokio::fs::symlink(TMPFILES_SRC, TMPFILES_DEST) .await .map_err(|e| { - Self::Error::Symlink( + ConfigureNixDaemonServiceError::Symlink( PathBuf::from(TMPFILES_SRC), PathBuf::from(TMPFILES_DEST), e, ) + .boxed() })?; execute_command( @@ -118,19 +119,19 @@ impl Actionable for ConfigureNixDaemonService { .arg("--prefix=/nix/var/nix"), ) .await - .map_err(Self::Error::CommandFailed)?; + .map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?; execute_command(Command::new("systemctl").arg("link").arg(SERVICE_SRC)) .await - .map_err(Self::Error::CommandFailed)?; + .map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?; execute_command(Command::new("systemctl").arg("link").arg(SOCKET_SRC)) .await - .map_err(Self::Error::CommandFailed)?; + .map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?; execute_command(Command::new("systemctl").arg("daemon-reload")) .await - .map_err(Self::Error::CommandFailed)?; + .map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?; }, }; @@ -156,7 +157,7 @@ impl Actionable for ConfigureNixDaemonService { } #[tracing::instrument(skip_all)] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { action_state } = self; if *action_state == ActionState::Uncompleted { tracing::trace!("Already reverted: Unconfiguring nix daemon service"); @@ -167,11 +168,11 @@ impl Actionable for ConfigureNixDaemonService { // We don't need to do this! Systemd does it for us! (In fact, it's an error if we try to do this...) execute_command(Command::new("systemctl").args(["disable", SOCKET_SRC])) .await - .map_err(Self::Error::CommandFailed)?; + .map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?; execute_command(Command::new("systemctl").args(["disable", SERVICE_SRC])) .await - .map_err(Self::Error::CommandFailed)?; + .map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?; execute_command( Command::new("systemd-tmpfiles") @@ -179,15 +180,15 @@ impl Actionable for ConfigureNixDaemonService { .arg("--prefix=/nix/var/nix"), ) .await - .map_err(Self::Error::CommandFailed)?; + .map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?; - remove_file(TMPFILES_DEST) - .await - .map_err(|e| Self::Error::RemoveFile(PathBuf::from(TMPFILES_DEST), e))?; + remove_file(TMPFILES_DEST).await.map_err(|e| { + ConfigureNixDaemonServiceError::RemoveFile(PathBuf::from(TMPFILES_DEST), e).boxed() + })?; execute_command(Command::new("systemctl").arg("daemon-reload")) .await - .map_err(Self::Error::CommandFailed)?; + .map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?; tracing::trace!("Unconfigured nix daemon service"); *action_state = ActionState::Uncompleted; @@ -195,12 +196,6 @@ impl Actionable for ConfigureNixDaemonService { } } -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}`")] diff --git a/src/actions/base/create_directory.rs b/src/actions/base/create_directory.rs index 4a5f8d1..c20b5e9 100644 --- a/src/actions/base/create_directory.rs +++ b/src/actions/base/create_directory.rs @@ -5,7 +5,7 @@ use nix::unistd::{chown, Group, User}; use serde::Serialize; use tokio::fs::{create_dir, remove_dir_all}; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct CreateDirectory { @@ -25,16 +25,16 @@ impl CreateDirectory { group: impl Into>, mode: impl Into>, force_prune_on_revert: bool, - ) -> Result { + ) -> Result> { let path = path.as_ref(); let user = user.into(); let group = group.into(); let mode = mode.into(); let action_state = if path.exists() { - let metadata = tokio::fs::metadata(path) - .await - .map_err(|e| CreateDirectoryError::GettingMetadata(path.to_path_buf(), e))?; + let metadata = tokio::fs::metadata(path).await.map_err(|e| { + CreateDirectoryError::GettingMetadata(path.to_path_buf(), e).boxed() + })?; if metadata.is_dir() { // TODO: Validate owner/group... ActionState::Completed @@ -45,7 +45,8 @@ impl CreateDirectory { "Path `{}` already exists and is not directory", path.display() ), - ))); + )) + .boxed()); } } else { ActionState::Uncompleted @@ -63,9 +64,8 @@ impl CreateDirectory { } #[async_trait::async_trait] +#[typetag::serde(name = "create-directory")] impl Actionable for CreateDirectory { - type Error = CreateDirectoryError; - fn describe_execute(&self) -> Vec { let Self { path, @@ -91,7 +91,7 @@ impl Actionable for CreateDirectory { group = self.group, mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))), ))] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { path, user, @@ -109,8 +109,8 @@ impl Actionable for CreateDirectory { let gid = if let Some(group) = group { Some( Group::from_name(group.as_str()) - .map_err(|e| Self::Error::GroupId(group.clone(), e))? - .ok_or(Self::Error::NoGroup(group.clone()))? + .map_err(|e| CreateDirectoryError::GroupId(group.clone(), e).boxed())? + .ok_or(CreateDirectoryError::NoGroup(group.clone()).boxed())? .gid, ) } else { @@ -119,8 +119,8 @@ impl Actionable for CreateDirectory { let uid = if let Some(user) = user { Some( User::from_name(user.as_str()) - .map_err(|e| Self::Error::UserId(user.clone(), e))? - .ok_or(Self::Error::NoUser(user.clone()))? + .map_err(|e| CreateDirectoryError::UserId(user.clone(), e).boxed())? + .ok_or(CreateDirectoryError::NoUser(user.clone()).boxed())? .uid, ) } else { @@ -129,13 +129,15 @@ impl Actionable for CreateDirectory { create_dir(path.clone()) .await - .map_err(|e| Self::Error::Creating(path.clone(), e))?; - chown(path, uid, gid).map_err(|e| Self::Error::Chown(path.clone(), e))?; + .map_err(|e| CreateDirectoryError::Creating(path.clone(), e).boxed())?; + chown(path, uid, gid).map_err(|e| CreateDirectoryError::Chown(path.clone(), e).boxed())?; if let Some(mode) = mode { tokio::fs::set_permissions(&path, PermissionsExt::from_mode(*mode)) .await - .map_err(|e| Self::Error::SetPermissions(*mode, path.to_owned(), e))?; + .map_err(|e| { + CreateDirectoryError::SetPermissions(*mode, path.to_owned(), e).boxed() + })?; } tracing::trace!("Created directory"); @@ -176,7 +178,7 @@ impl Actionable for CreateDirectory { group = self.group, mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))), ))] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { path, user: _, @@ -195,13 +197,13 @@ impl Actionable for CreateDirectory { let is_empty = path .read_dir() - .map_err(|e| CreateDirectoryError::ReadDir(path.clone(), e))? + .map_err(|e| CreateDirectoryError::ReadDir(path.clone(), e).boxed())? .next() .is_some(); match (is_empty, force_prune_on_revert) { (true, _) | (false, true) => remove_dir_all(path.clone()) .await - .map_err(|e| Self::Error::Removing(path.clone(), e))?, + .map_err(|e| CreateDirectoryError::Removing(path.clone(), e).boxed())?, (false, false) => {}, }; @@ -211,12 +213,6 @@ impl Actionable for CreateDirectory { } } -impl From for Action { - fn from(v: CreateDirectory) -> Self { - Action::CreateDirectory(v) - } -} - #[derive(Debug, thiserror::Error, Serialize)] pub enum CreateDirectoryError { #[error(transparent)] diff --git a/src/actions/base/create_file.rs b/src/actions/base/create_file.rs index eef9a63..d739b5b 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::actions::{Action, ActionState}; +use crate::actions::{ActionError, ActionState}; use crate::actions::{ActionDescription, Actionable}; @@ -30,11 +30,11 @@ impl CreateFile { mode: impl Into>, buf: String, force: bool, - ) -> Result { + ) -> Result> { let path = path.as_ref().to_path_buf(); if path.exists() && !force { - return Err(CreateFileError::Exists(path.to_path_buf())); + return Err(CreateFileError::Exists(path.to_path_buf()).boxed()); } Ok(Self { @@ -50,9 +50,8 @@ impl CreateFile { } #[async_trait::async_trait] +#[typetag::serde(name = "create-file")] impl Actionable for CreateFile { - type Error = CreateFileError; - fn describe_execute(&self) -> Vec { let Self { path, @@ -79,7 +78,7 @@ impl Actionable for CreateFile { group = self.group, mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))), ))] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { path, user, @@ -105,17 +104,17 @@ impl Actionable for CreateFile { let mut file = options .open(&path) .await - .map_err(|e| Self::Error::OpenFile(path.to_owned(), e))?; + .map_err(|e| CreateFileError::OpenFile(path.to_owned(), e).boxed())?; file.write_all(buf.as_bytes()) .await - .map_err(|e| Self::Error::WriteFile(path.to_owned(), e))?; + .map_err(|e| CreateFileError::WriteFile(path.to_owned(), e).boxed())?; let gid = if let Some(group) = group { Some( Group::from_name(group.as_str()) - .map_err(|e| Self::Error::GroupId(group.clone(), e))? - .ok_or(Self::Error::NoGroup(group.clone()))? + .map_err(|e| CreateFileError::GroupId(group.clone(), e).boxed())? + .ok_or(CreateFileError::NoGroup(group.clone()).boxed())? .gid, ) } else { @@ -124,14 +123,14 @@ impl Actionable for CreateFile { let uid = if let Some(user) = user { Some( User::from_name(user.as_str()) - .map_err(|e| Self::Error::UserId(user.clone(), e))? - .ok_or(Self::Error::NoUser(user.clone()))? + .map_err(|e| CreateFileError::UserId(user.clone(), e).boxed())? + .ok_or(CreateFileError::NoUser(user.clone()).boxed())? .uid, ) } else { None }; - chown(path, uid, gid).map_err(|e| Self::Error::Chown(path.clone(), e))?; + chown(path, uid, gid).map_err(|e| CreateFileError::Chown(path.clone(), e).boxed())?; tracing::trace!("Created file"); *action_state = ActionState::Completed; @@ -164,7 +163,7 @@ impl Actionable for CreateFile { group = self.group, mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))), ))] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { path, user: _, @@ -182,7 +181,7 @@ impl Actionable for CreateFile { remove_file(&path) .await - .map_err(|e| Self::Error::RemoveFile(path.to_owned(), e))?; + .map_err(|e| CreateFileError::RemoveFile(path.to_owned(), e).boxed())?; tracing::trace!("Deleted file"); *action_state = ActionState::Uncompleted; @@ -190,12 +189,6 @@ impl Actionable for CreateFile { } } -impl From for Action { - fn from(v: CreateFile) -> Self { - Action::CreateFile(v) - } -} - #[derive(Debug, thiserror::Error, Serialize)] pub enum CreateFileError { #[error("File exists `{0}`")] diff --git a/src/actions/base/create_group.rs b/src/actions/base/create_group.rs index 28db7ee..4f33bd2 100644 --- a/src/actions/base/create_group.rs +++ b/src/actions/base/create_group.rs @@ -3,7 +3,7 @@ use tokio::process::Command; use crate::execute_command; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct CreateGroup { @@ -24,9 +24,8 @@ impl CreateGroup { } #[async_trait::async_trait] +#[typetag::serde(name = "create-group")] impl Actionable for CreateGroup { - type Error = CreateGroupError; - fn describe_execute(&self) -> Vec { let Self { name, @@ -49,7 +48,7 @@ impl Actionable for CreateGroup { user = self.name, gid = self.gid, ))] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { name, gid, @@ -79,7 +78,7 @@ impl Actionable for CreateGroup { name.as_str(), ])) .await - .map_err(Self::Error::Command)?; + .map_err(|e| CreateGroupError::Command(e).boxed())?; }, _ => { execute_command(Command::new("groupadd").args([ @@ -89,7 +88,7 @@ impl Actionable for CreateGroup { &name, ])) .await - .map_err(CreateGroupError::Command)?; + .map_err(|e| CreateGroupError::Command(e).boxed())?; }, }; @@ -120,7 +119,7 @@ impl Actionable for CreateGroup { user = self.name, gid = self.gid, ))] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { name, gid: _, @@ -142,12 +141,12 @@ impl Actionable for CreateGroup { | OperatingSystem::Darwin => { execute_command(Command::new("groupdel").arg(&name)) .await - .map_err(CreateGroupError::Command)?; + .map_err(|e| CreateGroupError::Command(e).boxed())?; }, _ => { - execute_command(Command::new("userdel").args([&name.to_string()])) + execute_command(Command::new("groupdel").arg(&name)) .await - .map_err(Self::Error::Command)?; + .map_err(|e| CreateGroupError::Command(e).boxed())?; }, }; @@ -157,12 +156,6 @@ impl Actionable for CreateGroup { } } -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")] diff --git a/src/actions/base/create_or_append_file.rs b/src/actions/base/create_or_append_file.rs index 80e59c9..0240c96 100644 --- a/src/actions/base/create_or_append_file.rs +++ b/src/actions/base/create_or_append_file.rs @@ -10,7 +10,7 @@ use tokio::{ io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt}, }; -use crate::actions::{Action, ActionState}; +use crate::actions::{ActionError, ActionState}; use crate::actions::{ActionDescription, Actionable}; @@ -47,9 +47,8 @@ impl CreateOrAppendFile { } #[async_trait::async_trait] +#[typetag::serde(name = "create-or-append-file")] impl Actionable for CreateOrAppendFile { - type Error = CreateOrAppendFileError; - fn describe_execute(&self) -> Vec { let Self { path, @@ -75,7 +74,7 @@ impl Actionable for CreateOrAppendFile { group = self.group, mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))), ))] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { path, user, @@ -96,21 +95,21 @@ impl Actionable for CreateOrAppendFile { .read(true) .open(&path) .await - .map_err(|e| Self::Error::OpenFile(path.to_owned(), e))?; + .map_err(|e| CreateOrAppendFileError::OpenFile(path.to_owned(), e).boxed())?; file.seek(SeekFrom::End(0)) .await - .map_err(|e| Self::Error::SeekFile(path.to_owned(), e))?; + .map_err(|e| CreateOrAppendFileError::SeekFile(path.to_owned(), e).boxed())?; file.write_all(buf.as_bytes()) .await - .map_err(|e| Self::Error::WriteFile(path.to_owned(), e))?; + .map_err(|e| CreateOrAppendFileError::WriteFile(path.to_owned(), e).boxed())?; let gid = if let Some(group) = group { Some( Group::from_name(group.as_str()) - .map_err(|e| Self::Error::GroupId(group.clone(), e))? - .ok_or(Self::Error::NoGroup(group.clone()))? + .map_err(|e| CreateOrAppendFileError::GroupId(group.clone(), e).boxed())? + .ok_or(CreateOrAppendFileError::NoGroup(group.clone()).boxed())? .gid, ) } else { @@ -119,8 +118,8 @@ impl Actionable for CreateOrAppendFile { let uid = if let Some(user) = user { Some( User::from_name(user.as_str()) - .map_err(|e| Self::Error::UserId(user.clone(), e))? - .ok_or(Self::Error::NoUser(user.clone()))? + .map_err(|e| CreateOrAppendFileError::UserId(user.clone(), e).boxed())? + .ok_or(CreateOrAppendFileError::NoUser(user.clone()).boxed())? .uid, ) } else { @@ -130,10 +129,13 @@ impl Actionable for CreateOrAppendFile { if let Some(mode) = mode { tokio::fs::set_permissions(&path, PermissionsExt::from_mode(*mode)) .await - .map_err(|e| Self::Error::SetPermissions(*mode, path.to_owned(), e))?; + .map_err(|e| { + CreateOrAppendFileError::SetPermissions(*mode, path.to_owned(), e).boxed() + })?; } - chown(path, uid, gid).map_err(|e| Self::Error::Chown(path.clone(), e))?; + chown(path, uid, gid) + .map_err(|e| CreateOrAppendFileError::Chown(path.clone(), e).boxed())?; tracing::trace!("Created or appended fragment to file"); *action_state = ActionState::Completed; @@ -168,7 +170,7 @@ impl Actionable for CreateOrAppendFile { group = self.group, mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))), ))] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { path, user: _, @@ -189,12 +191,12 @@ impl Actionable for CreateOrAppendFile { .read(true) .open(&path) .await - .map_err(|e| Self::Error::ReadFile(path.to_owned(), e))?; + .map_err(|e| CreateOrAppendFileError::ReadFile(path.to_owned(), e).boxed())?; let mut file_contents = String::default(); file.read_to_string(&mut file_contents) .await - .map_err(|e| Self::Error::SeekFile(path.to_owned(), e))?; + .map_err(|e| CreateOrAppendFileError::SeekFile(path.to_owned(), e).boxed())?; if let Some(start) = file_contents.rfind(buf.as_str()) { let end = start + buf.len(); @@ -204,16 +206,16 @@ impl Actionable for CreateOrAppendFile { if buf.is_empty() { remove_file(&path) .await - .map_err(|e| Self::Error::RemoveFile(path.to_owned(), e))?; + .map_err(|e| CreateOrAppendFileError::RemoveFile(path.to_owned(), e).boxed())?; tracing::trace!("Removed file (since all content was removed)"); } else { file.seek(SeekFrom::Start(0)) .await - .map_err(|e| Self::Error::SeekFile(path.to_owned(), e))?; + .map_err(|e| CreateOrAppendFileError::SeekFile(path.to_owned(), e).boxed())?; file.write_all(file_contents.as_bytes()) .await - .map_err(|e| Self::Error::WriteFile(path.to_owned(), e))?; + .map_err(|e| CreateOrAppendFileError::WriteFile(path.to_owned(), e).boxed())?; tracing::trace!("Removed fragment from from file"); } @@ -222,12 +224,6 @@ impl Actionable for CreateOrAppendFile { } } -impl From for Action { - fn from(v: CreateOrAppendFile) -> Self { - Action::CreateOrAppendFile(v) - } -} - #[derive(Debug, thiserror::Error, Serialize)] pub enum CreateOrAppendFileError { #[error("Remove file `{0}`")] diff --git a/src/actions/base/create_user.rs b/src/actions/base/create_user.rs index 47d6abc..79ec9a9 100644 --- a/src/actions/base/create_user.rs +++ b/src/actions/base/create_user.rs @@ -3,7 +3,7 @@ use tokio::process::Command; use crate::execute_command; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct CreateUser { @@ -28,9 +28,8 @@ impl CreateUser { } #[async_trait::async_trait] +#[typetag::serde(name = "create-user")] impl Actionable for CreateUser { - type Error = CreateUserError; - fn describe_execute(&self) -> Vec { if self.action_state == ActionState::Completed { vec![] @@ -58,7 +57,7 @@ impl Actionable for CreateUser { groupname = self.groupname, gid = self.gid, ))] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { name, uid, @@ -86,7 +85,7 @@ impl Actionable for CreateUser { &format!("/Users/{name}"), ])) .await - .map_err(Self::Error::Command)?; + .map_err(|e| CreateUserError::Command(e).boxed())?; execute_command(Command::new("/usr/bin/dscl").args([ ".", "-create", @@ -95,7 +94,7 @@ impl Actionable for CreateUser { &format!("{uid}"), ])) .await - .map_err(Self::Error::Command)?; + .map_err(|e| CreateUserError::Command(e).boxed())?; execute_command(Command::new("/usr/bin/dscl").args([ ".", "-create", @@ -104,7 +103,7 @@ impl Actionable for CreateUser { &format!("{gid}"), ])) .await - .map_err(Self::Error::Command)?; + .map_err(|e| CreateUserError::Command(e).boxed())?; execute_command(Command::new("/usr/bin/dscl").args([ ".", "-create", @@ -113,7 +112,7 @@ impl Actionable for CreateUser { "/var/empty", ])) .await - .map_err(Self::Error::Command)?; + .map_err(|e| CreateUserError::Command(e).boxed())?; execute_command(Command::new("/usr/bin/dscl").args([ ".", "-create", @@ -122,7 +121,7 @@ impl Actionable for CreateUser { "/sbin/nologin", ])) .await - .map_err(Self::Error::Command)?; + .map_err(|e| CreateUserError::Command(e).boxed())?; execute_command( Command::new("/usr/bin/dscl") .args([ @@ -134,7 +133,7 @@ impl Actionable for CreateUser { .arg(&name), ) .await - .map_err(Self::Error::Command)?; + .map_err(|e| CreateUserError::Command(e).boxed())?; execute_command(Command::new("/usr/bin/dscl").args([ ".", "-create", @@ -143,7 +142,7 @@ impl Actionable for CreateUser { "1", ])) .await - .map_err(Self::Error::Command)?; + .map_err(|e| CreateUserError::Command(e).boxed())?; execute_command( Command::new("/usr/sbin/dseditgroup") .args(["-o", "edit"]) @@ -154,7 +153,7 @@ impl Actionable for CreateUser { .arg(groupname), ) .await - .map_err(Self::Error::Command)?; + .map_err(|e| CreateUserError::Command(e).boxed())?; }, _ => { execute_command(Command::new("useradd").args([ @@ -177,7 +176,7 @@ impl Actionable for CreateUser { &name.to_string(), ])) .await - .map_err(Self::Error::Command)?; + .map_err(|e| CreateUserError::Command(e).boxed())?; }, } @@ -212,7 +211,7 @@ impl Actionable for CreateUser { uid = self.uid, gid = self.gid, ))] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { name, uid: _, @@ -240,12 +239,12 @@ impl Actionable for CreateUser { &format!("/Users/{name}"), ])) .await - .map_err(Self::Error::Command)?; + .map_err(|e| CreateUserError::Command(e).boxed())?; }, _ => { execute_command(Command::new("userdel").args([&name.to_string()])) .await - .map_err(Self::Error::Command)?; + .map_err(|e| CreateUserError::Command(e).boxed())?; }, }; @@ -255,12 +254,6 @@ impl Actionable for CreateUser { } } -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")] diff --git a/src/actions/base/darwin/bootstrap_volume.rs b/src/actions/base/darwin/bootstrap_volume.rs index 2321b29..70916a4 100644 --- a/src/actions/base/darwin/bootstrap_volume.rs +++ b/src/actions/base/darwin/bootstrap_volume.rs @@ -5,7 +5,7 @@ use tokio::process::Command; use crate::execute_command; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct BootstrapVolume { @@ -15,7 +15,9 @@ pub struct BootstrapVolume { impl BootstrapVolume { #[tracing::instrument(skip_all)] - pub async fn plan(path: impl AsRef) -> Result { + pub async fn plan( + path: impl AsRef, + ) -> Result> { Ok(Self { path: path.as_ref().to_path_buf(), action_state: ActionState::Uncompleted, @@ -24,9 +26,8 @@ impl BootstrapVolume { } #[async_trait::async_trait] +#[typetag::serde(name = "bootstrap-volume")] impl Actionable for BootstrapVolume { - type Error = BootstrapVolumeError; - fn describe_execute(&self) -> Vec { if self.action_state == ActionState::Completed { vec![] @@ -41,7 +42,7 @@ impl Actionable for BootstrapVolume { #[tracing::instrument(skip_all, fields( path = %self.path.display(), ))] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { path, action_state } = self; if *action_state == ActionState::Completed { tracing::trace!("Already completed: Bootstrapping volume"); @@ -55,14 +56,14 @@ impl Actionable for BootstrapVolume { .arg(path), ) .await - .map_err(Self::Error::Command)?; + .map_err(|e| BootstrapVolumeError::Command(e).boxed())?; execute_command(Command::new("launchctl").args([ "kickstart", "-k", "system/org.nixos.darwin-store", ])) .await - .map_err(Self::Error::Command)?; + .map_err(|e| BootstrapVolumeError::Command(e).boxed())?; tracing::trace!("Bootstrapped volume"); *action_state = ActionState::Completed; @@ -83,7 +84,7 @@ impl Actionable for BootstrapVolume { #[tracing::instrument(skip_all, fields( path = %self.path.display(), ))] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { path, action_state } = self; if *action_state == ActionState::Uncompleted { tracing::trace!("Already reverted: Stop volume"); @@ -97,7 +98,7 @@ impl Actionable for BootstrapVolume { .arg(path), ) .await - .map_err(Self::Error::Command)?; + .map_err(|e| BootstrapVolumeError::Command(e).boxed())?; tracing::trace!("Stopped volume"); *action_state = ActionState::Completed; @@ -105,12 +106,6 @@ impl Actionable for BootstrapVolume { } } -impl From for Action { - fn from(v: BootstrapVolume) -> Self { - Action::DarwinBootstrapVolume(v) - } -} - #[derive(Debug, thiserror::Error, Serialize)] pub enum BootstrapVolumeError { #[error("Failed to execute command")] diff --git a/src/actions/base/darwin/create_synthetic_objects.rs b/src/actions/base/darwin/create_synthetic_objects.rs index fb6928e..4a55d39 100644 --- a/src/actions/base/darwin/create_synthetic_objects.rs +++ b/src/actions/base/darwin/create_synthetic_objects.rs @@ -3,7 +3,7 @@ use tokio::process::Command; use crate::execute_command; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct CreateSyntheticObjects { @@ -12,7 +12,7 @@ pub struct CreateSyntheticObjects { impl CreateSyntheticObjects { #[tracing::instrument(skip_all)] - pub async fn plan() -> Result { + pub async fn plan() -> Result> { Ok(Self { action_state: ActionState::Uncompleted, }) @@ -20,9 +20,8 @@ impl CreateSyntheticObjects { } #[async_trait::async_trait] +#[typetag::serde(name = "create-synthetic-objects")] impl Actionable for CreateSyntheticObjects { - type Error = CreateSyntheticObjectsError; - fn describe_execute(&self) -> Vec { if self.action_state == ActionState::Completed { vec![] @@ -35,7 +34,7 @@ impl Actionable for CreateSyntheticObjects { } #[tracing::instrument(skip_all, fields())] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { action_state } = self; if *action_state == ActionState::Completed { tracing::trace!("Already completed: Creating synthetic objects"); @@ -74,7 +73,7 @@ impl Actionable for CreateSyntheticObjects { } #[tracing::instrument(skip_all, fields())] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { action_state } = self; if *action_state == ActionState::Uncompleted { tracing::trace!("Already reverted: Refreshing synthetic objects"); @@ -102,12 +101,6 @@ impl Actionable for CreateSyntheticObjects { } } -impl From for Action { - fn from(v: CreateSyntheticObjects) -> Self { - Action::DarwinCreateSyntheticObjects(v) - } -} - #[derive(Debug, thiserror::Error, Serialize)] pub enum CreateSyntheticObjectsError { #[error("Failed to execute command")] diff --git a/src/actions/base/darwin/create_volume.rs b/src/actions/base/darwin/create_volume.rs index b4ee4a1..9f18c55 100644 --- a/src/actions/base/darwin/create_volume.rs +++ b/src/actions/base/darwin/create_volume.rs @@ -5,7 +5,7 @@ use tokio::process::Command; use crate::execute_command; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct CreateVolume { @@ -21,7 +21,7 @@ impl CreateVolume { disk: impl AsRef, name: String, case_sensitive: bool, - ) -> Result { + ) -> Result> { Ok(Self { disk: disk.as_ref().to_path_buf(), name, @@ -32,9 +32,8 @@ impl CreateVolume { } #[async_trait::async_trait] +#[typetag::serde(name = "create-volume")] impl Actionable for CreateVolume { - type Error = CreateVolumeError; - fn describe_execute(&self) -> Vec { if self.action_state == ActionState::Completed { vec![] @@ -55,7 +54,7 @@ impl Actionable for CreateVolume { name = %self.name, case_sensitive = %self.case_sensitive, ))] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { disk, name, @@ -81,7 +80,7 @@ impl Actionable for CreateVolume { "-nomount", ])) .await - .map_err(Self::Error::Command)?; + .map_err(|e| CreateVolumeError::Command(e).boxed())?; tracing::trace!("Created volume"); *action_state = ActionState::Completed; @@ -108,7 +107,7 @@ impl Actionable for CreateVolume { name = %self.name, case_sensitive = %self.case_sensitive, ))] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { disk: _, name, @@ -123,7 +122,7 @@ impl Actionable for CreateVolume { execute_command(Command::new("/usr/sbin/diskutil").args(["apfs", "deleteVolume", name])) .await - .map_err(Self::Error::Command)?; + .map_err(|e| CreateVolumeError::Command(e).boxed())?; tracing::trace!("Deleted volume"); *action_state = ActionState::Completed; @@ -131,12 +130,6 @@ impl Actionable for CreateVolume { } } -impl From for Action { - fn from(v: CreateVolume) -> Self { - Action::DarwinCreateVolume(v) - } -} - #[derive(Debug, thiserror::Error, Serialize)] pub enum CreateVolumeError { #[error("Failed to execute command")] diff --git a/src/actions/base/darwin/enable_ownership.rs b/src/actions/base/darwin/enable_ownership.rs index b33a56a..92457f6 100644 --- a/src/actions/base/darwin/enable_ownership.rs +++ b/src/actions/base/darwin/enable_ownership.rs @@ -6,7 +6,7 @@ use tokio::process::Command; use crate::execute_command; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; use crate::os::darwin::DiskUtilOutput; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] @@ -17,7 +17,9 @@ pub struct EnableOwnership { impl EnableOwnership { #[tracing::instrument(skip_all)] - pub async fn plan(path: impl AsRef) -> Result { + pub async fn plan( + path: impl AsRef, + ) -> Result> { Ok(Self { path: path.as_ref().to_path_buf(), action_state: ActionState::Uncompleted, @@ -26,9 +28,8 @@ impl EnableOwnership { } #[async_trait::async_trait] +#[typetag::serde(name = "enable-ownership")] impl Actionable for EnableOwnership { - type Error = EnableOwnershipError; - fn describe_execute(&self) -> Vec { if self.action_state == ActionState::Completed { vec![] @@ -43,7 +44,7 @@ impl Actionable for EnableOwnership { #[tracing::instrument(skip_all, fields( path = %self.path.display(), ))] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { path, action_state } = self; if *action_state == ActionState::Completed { tracing::trace!("Already completed: Enabling ownership"); @@ -72,7 +73,7 @@ impl Actionable for EnableOwnership { .arg(path), ) .await - .map_err(Self::Error::Command)?; + .map_err(|e| EnableOwnershipError::Command(e).boxed())?; } tracing::trace!("Enabled ownership"); @@ -91,7 +92,7 @@ impl Actionable for EnableOwnership { #[tracing::instrument(skip_all, fields( path = %self.path.display(), ))] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { path: _, action_state, @@ -108,12 +109,6 @@ impl Actionable for EnableOwnership { } } -impl From for Action { - fn from(v: EnableOwnership) -> Self { - Action::DarwinEnableOwnership(v) - } -} - #[derive(Debug, thiserror::Error, Serialize)] pub enum EnableOwnershipError { #[error("Failed to execute command")] diff --git a/src/actions/base/darwin/encrypt_volume.rs b/src/actions/base/darwin/encrypt_volume.rs index 2c95563..b564022 100644 --- a/src/actions/base/darwin/encrypt_volume.rs +++ b/src/actions/base/darwin/encrypt_volume.rs @@ -1,7 +1,7 @@ use serde::Serialize; use std::path::{Path, PathBuf}; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct EncryptVolume { @@ -15,7 +15,7 @@ impl EncryptVolume { pub async fn plan( disk: impl AsRef, password: String, - ) -> Result { + ) -> Result> { Ok(Self { disk: disk.as_ref().to_path_buf(), password, @@ -25,9 +25,8 @@ impl EncryptVolume { } #[async_trait::async_trait] +#[typetag::serde(name = "encrypt-volume")] impl Actionable for EncryptVolume { - type Error = EncryptVolumeError; - fn describe_execute(&self) -> Vec { if self.action_state == ActionState::Completed { vec![] @@ -42,7 +41,7 @@ impl Actionable for EncryptVolume { #[tracing::instrument(skip_all, fields( disk = %self.disk.display(), ))] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { disk: _, password: _, @@ -72,7 +71,7 @@ impl Actionable for EncryptVolume { #[tracing::instrument(skip_all, fields( disk = %self.disk.display(), ))] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { disk: _, password: _, @@ -90,12 +89,6 @@ impl Actionable for EncryptVolume { } } -impl From for Action { - fn from(v: EncryptVolume) -> Self { - Action::DarwinEncryptVolume(v) - } -} - #[derive(Debug, thiserror::Error, Serialize)] pub enum EncryptVolumeError { #[error("Failed to execute command")] diff --git a/src/actions/base/darwin/kickstart_launchctl_service.rs b/src/actions/base/darwin/kickstart_launchctl_service.rs index c4e30ac..08593ed 100644 --- a/src/actions/base/darwin/kickstart_launchctl_service.rs +++ b/src/actions/base/darwin/kickstart_launchctl_service.rs @@ -3,7 +3,7 @@ use tokio::process::Command; use crate::execute_command; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct KickstartLaunchctlService { @@ -13,7 +13,7 @@ pub struct KickstartLaunchctlService { impl KickstartLaunchctlService { #[tracing::instrument(skip_all)] - pub async fn plan(unit: String) -> Result { + pub async fn plan(unit: String) -> Result> { Ok(Self { unit, action_state: ActionState::Uncompleted, @@ -22,9 +22,8 @@ impl KickstartLaunchctlService { } #[async_trait::async_trait] +#[typetag::serde(name = "kickstart-launchctl-service")] impl Actionable for KickstartLaunchctlService { - type Error = KickstartLaunchctlServiceError; - fn describe_execute(&self) -> Vec { let Self { unit, action_state } = self; if *action_state == ActionState::Completed { @@ -42,7 +41,7 @@ impl Actionable for KickstartLaunchctlService { #[tracing::instrument(skip_all, fields( unit = %self.unit, ))] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { unit, action_state } = self; if *action_state == ActionState::Completed { tracing::trace!("Already completed: Kickstarting launchctl unit"); @@ -57,7 +56,7 @@ impl Actionable for KickstartLaunchctlService { .arg(unit), ) .await - .map_err(KickstartLaunchctlServiceError::Command)?; + .map_err(|e| KickstartLaunchctlServiceError::Command(e).boxed())?; tracing::trace!("Kickstarted launchctl unit"); *action_state = ActionState::Completed; @@ -80,7 +79,7 @@ impl Actionable for KickstartLaunchctlService { #[tracing::instrument(skip_all, fields( unit = %self.unit, ))] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { unit, action_state } = self; if *action_state == ActionState::Uncompleted { tracing::trace!("Already reverted: Stopping launchctl unit"); @@ -90,7 +89,7 @@ impl Actionable for KickstartLaunchctlService { execute_command(Command::new("launchctl").arg("stop").arg(unit)) .await - .map_err(KickstartLaunchctlServiceError::Command)?; + .map_err(|e| KickstartLaunchctlServiceError::Command(e).boxed())?; tracing::trace!("Stopped launchctl unit"); *action_state = ActionState::Completed; @@ -98,12 +97,6 @@ impl Actionable for KickstartLaunchctlService { } } -impl From for Action { - fn from(v: KickstartLaunchctlService) -> Self { - Action::DarwinKickStartLaunchctlService(v) - } -} - #[derive(Debug, thiserror::Error, Serialize)] pub enum KickstartLaunchctlServiceError { #[error("Failed to execute command")] diff --git a/src/actions/base/darwin/unmount_volume.rs b/src/actions/base/darwin/unmount_volume.rs index 2827944..4710c9e 100644 --- a/src/actions/base/darwin/unmount_volume.rs +++ b/src/actions/base/darwin/unmount_volume.rs @@ -5,7 +5,7 @@ use tokio::process::Command; use crate::execute_command; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct UnmountVolume { @@ -16,7 +16,10 @@ pub struct UnmountVolume { impl UnmountVolume { #[tracing::instrument(skip_all)] - pub async fn plan(disk: impl AsRef, name: String) -> Result { + pub async fn plan( + disk: impl AsRef, + name: String, + ) -> Result> { let disk = disk.as_ref().to_owned(); Ok(Self { disk, @@ -27,9 +30,8 @@ impl UnmountVolume { } #[async_trait::async_trait] +#[typetag::serde(name = "unmount-volume")] impl Actionable for UnmountVolume { - type Error = UnmountVolumeError; - fn describe_execute(&self) -> Vec { if self.action_state == ActionState::Completed { vec![] @@ -47,7 +49,7 @@ impl Actionable for UnmountVolume { disk = %self.disk.display(), name = %self.name, ))] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { disk: _, name, @@ -65,7 +67,7 @@ impl Actionable for UnmountVolume { .arg(name), ) .await - .map_err(Self::Error::Command)?; + .map_err(|e| UnmountVolumeError::Command(e).boxed())?; tracing::trace!("Unmounted volume"); *action_state = ActionState::Completed; @@ -89,7 +91,7 @@ impl Actionable for UnmountVolume { disk = %self.disk.display(), name = %self.name, ))] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { disk: _, name, @@ -107,7 +109,7 @@ impl Actionable for UnmountVolume { .arg(name), ) .await - .map_err(Self::Error::Command)?; + .map_err(|e| UnmountVolumeError::Command(e).boxed())?; tracing::trace!("Stopped systemd unit"); *action_state = ActionState::Completed; @@ -115,12 +117,6 @@ impl Actionable for UnmountVolume { } } -impl From for Action { - fn from(v: UnmountVolume) -> Self { - Action::DarwinUnmountVolume(v) - } -} - #[derive(Debug, thiserror::Error, Serialize)] pub enum UnmountVolumeError { #[error("Failed to execute command")] diff --git a/src/actions/base/fetch_nix.rs b/src/actions/base/fetch_nix.rs index 0b4dbad..d9947b9 100644 --- a/src/actions/base/fetch_nix.rs +++ b/src/actions/base/fetch_nix.rs @@ -5,7 +5,7 @@ use reqwest::Url; use serde::Serialize; use tokio::task::JoinError; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct FetchNix { @@ -29,9 +29,8 @@ impl FetchNix { } #[async_trait::async_trait] +#[typetag::serde(name = "fetch-nix")] impl Actionable for FetchNix { - type Error = FetchNixError; - fn describe_execute(&self) -> Vec { let Self { url, @@ -52,7 +51,7 @@ impl Actionable for FetchNix { url = %self.url, dest = %self.dest.display(), ))] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { url, dest, @@ -66,8 +65,11 @@ impl Actionable for FetchNix { let res = reqwest::get(url.clone()) .await - .map_err(Self::Error::Reqwest)?; - let bytes = res.bytes().await.map_err(Self::Error::Reqwest)?; + .map_err(|e| FetchNixError::Reqwest(e).boxed())?; + let bytes = res + .bytes() + .await + .map_err(|e| FetchNixError::Reqwest(e).boxed())?; // TODO(@Hoverbear): Pick directory tracing::trace!("Unpacking tar.xz"); let dest_clone = dest.clone(); @@ -76,7 +78,7 @@ impl Actionable for FetchNix { let mut archive = tar::Archive::new(decoder); archive .unpack(&dest_clone) - .map_err(Self::Error::Unarchive)?; + .map_err(|e| FetchNixError::Unarchive(e).boxed())?; tracing::trace!("Fetched Nix"); *action_state = ActionState::Completed; @@ -95,7 +97,7 @@ impl Actionable for FetchNix { url = %self.url, dest = %self.dest.display(), ))] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { url: _, dest: _, @@ -112,12 +114,6 @@ impl Actionable for FetchNix { } } -impl From for Action { - fn from(v: FetchNix) -> Self { - Action::FetchNix(v) - } -} - #[derive(Debug, thiserror::Error, Serialize)] pub enum FetchNixError { #[error("Joining spawned async task")] diff --git a/src/actions/base/move_unpacked_nix.rs b/src/actions/base/move_unpacked_nix.rs index 5a75f12..d5d7898 100644 --- a/src/actions/base/move_unpacked_nix.rs +++ b/src/actions/base/move_unpacked_nix.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use serde::Serialize; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; const DEST: &str = "/nix/store"; @@ -24,9 +24,8 @@ impl MoveUnpackedNix { } #[async_trait::async_trait] +#[typetag::serde(name = "mount-unpacked-nix")] impl Actionable for MoveUnpackedNix { - type Error = MoveUnpackedNixError; - fn describe_execute(&self) -> Vec { if self.action_state == ActionState::Completed { vec![] @@ -45,7 +44,7 @@ impl Actionable for MoveUnpackedNix { src = %self.src.display(), dest = DEST, ))] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { src, action_state } = self; if *action_state == ActionState::Completed { tracing::trace!("Already completed: Moving Nix"); @@ -54,8 +53,10 @@ impl Actionable for MoveUnpackedNix { tracing::debug!("Moving Nix"); // TODO(@Hoverbear): I would like to make this less awful - let found_nix_paths = - glob::glob(&format!("{}/nix-*", src.display()))?.collect::, _>>()?; + let found_nix_paths = glob::glob(&format!("{}/nix-*", src.display())) + .map_err(|e| e.boxed())? + .collect::, _>>() + .map_err(|e| e.boxed())?; assert_eq!( found_nix_paths.len(), 1, @@ -67,11 +68,13 @@ impl Actionable for MoveUnpackedNix { let dest = Path::new(DEST); tokio::fs::rename(src_store.clone(), dest) .await - .map_err(|e| MoveUnpackedNixError::Rename(src_store.clone(), dest.to_owned(), e))?; + .map_err(|e| { + MoveUnpackedNixError::Rename(src_store.clone(), dest.to_owned(), e).boxed() + })?; tokio::fs::remove_dir_all(src) .await - .map_err(|e| MoveUnpackedNixError::Rename(src_store, dest.to_owned(), e))?; + .map_err(|e| MoveUnpackedNixError::Rename(src_store, dest.to_owned(), e).boxed())?; tracing::trace!("Moved Nix"); *action_state = ActionState::Completed; Ok(()) @@ -89,7 +92,7 @@ impl Actionable for MoveUnpackedNix { src = %self.src.display(), dest = DEST, ))] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { src: _, action_state, @@ -104,12 +107,6 @@ impl Actionable for MoveUnpackedNix { } } -impl From for Action { - fn from(v: MoveUnpackedNix) -> Self { - Action::MoveUnpackedNix(v) - } -} - #[derive(Debug, thiserror::Error, Serialize)] pub enum MoveUnpackedNixError { #[error("Glob pattern error")] diff --git a/src/actions/base/setup_default_profile.rs b/src/actions/base/setup_default_profile.rs index 93d1150..4c5f5b8 100644 --- a/src/actions/base/setup_default_profile.rs +++ b/src/actions/base/setup_default_profile.rs @@ -1,5 +1,5 @@ use crate::{ - actions::{Action, ActionState}, + actions::{ActionError, ActionState}, execute_command, set_env, }; @@ -26,9 +26,8 @@ impl SetupDefaultProfile { } #[async_trait::async_trait] +#[typetag::serde(name = "setup-default-profile")] impl Actionable for SetupDefaultProfile { - type Error = SetupDefaultProfileError; - fn describe_execute(&self) -> Vec { if self.action_state == ActionState::Completed { vec![] @@ -43,7 +42,7 @@ impl Actionable for SetupDefaultProfile { #[tracing::instrument(skip_all, fields( channels = %self.channels.join(","), ))] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { channels, action_state, @@ -57,7 +56,9 @@ impl Actionable for SetupDefaultProfile { // 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(Self::Error::GlobPatternError)? { + for entry in + glob(nix_pkg_glob).map_err(|e| SetupDefaultProfileError::GlobPatternError(e).boxed())? + { match entry { Ok(path) => { // TODO(@Hoverbear): Should probably ensure is unique @@ -70,13 +71,15 @@ impl Actionable for SetupDefaultProfile { let nix_pkg = if let Some(nix_pkg) = found_nix_pkg { nix_pkg } else { - return Err(Self::Error::NoNssCacert); // TODO(@hoverbear): Fix this error + return Err(Box::new(SetupDefaultProfileError::NoNssCacert)); // TODO(@hoverbear): Fix this error }; // 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(Self::Error::GlobPatternError)? { + for entry in glob(nss_ca_cert_pkg_glob) + .map_err(|e| SetupDefaultProfileError::GlobPatternError(e).boxed())? + { match entry { Ok(path) => { // TODO(@Hoverbear): Should probably ensure is unique @@ -89,7 +92,7 @@ impl Actionable for SetupDefaultProfile { let nss_ca_cert_pkg = if let Some(nss_ca_cert_pkg) = found_nss_ca_cert_pkg { nss_ca_cert_pkg } else { - return Err(Self::Error::NoNssCacert); + return Err(Box::new(SetupDefaultProfileError::NoNssCacert)); }; // Install `nix` itself into the store @@ -97,14 +100,17 @@ impl Actionable for SetupDefaultProfile { Command::new(nix_pkg.join("bin/nix-env")) .arg("-i") .arg(&nix_pkg) - .env("HOME", dirs::home_dir().ok_or(Self::Error::NoRootHome)?) + .env( + "HOME", + dirs::home_dir().ok_or_else(|| SetupDefaultProfileError::NoRootHome.boxed())?, + ) .env( "NIX_SSL_CERT_FILE", nss_ca_cert_pkg.join("etc/ssl/certs/ca-bundle.crt"), ), /* This is apparently load bearing... */ ) .await - .map_err(SetupDefaultProfileError::Command)?; + .map_err(|e| SetupDefaultProfileError::Command(e).boxed())?; // Install `nss-cacert` into the store execute_command( @@ -117,7 +123,7 @@ impl Actionable for SetupDefaultProfile { ), ) .await - .map_err(SetupDefaultProfileError::Command)?; + .map_err(|e| SetupDefaultProfileError::Command(e).boxed())?; set_env( "NIX_SSL_CERT_FILE", @@ -137,7 +143,7 @@ impl Actionable for SetupDefaultProfile { execute_command(&mut command) .await - .map_err(SetupDefaultProfileError::Command)?; + .map_err(|e| SetupDefaultProfileError::Command(e).boxed())?; } tracing::trace!("Set up default profile"); @@ -159,7 +165,7 @@ impl Actionable for SetupDefaultProfile { #[tracing::instrument(skip_all, fields( channels = %self.channels.join(","), ))] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { channels: _, action_state, @@ -178,12 +184,6 @@ impl Actionable for SetupDefaultProfile { } } -impl From for Action { - fn from(v: SetupDefaultProfile) -> Self { - Action::SetupDefaultProfile(v) - } -} - #[derive(Debug, thiserror::Error, Serialize)] pub enum SetupDefaultProfileError { #[error("Glob pattern error")] diff --git a/src/actions/base/start_systemd_unit.rs b/src/actions/base/start_systemd_unit.rs index 43294e1..1a5b7d9 100644 --- a/src/actions/base/start_systemd_unit.rs +++ b/src/actions/base/start_systemd_unit.rs @@ -3,7 +3,7 @@ use tokio::process::Command; use crate::execute_command; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct StartSystemdUnit { @@ -13,7 +13,7 @@ pub struct StartSystemdUnit { impl StartSystemdUnit { #[tracing::instrument(skip_all)] - pub async fn plan(unit: String) -> Result { + pub async fn plan(unit: String) -> Result> { Ok(Self { unit, action_state: ActionState::Uncompleted, @@ -22,9 +22,8 @@ impl StartSystemdUnit { } #[async_trait::async_trait] +#[typetag::serde(name = "start-systemd-unit")] impl Actionable for StartSystemdUnit { - type Error = StartSystemdUnitError; - fn describe_execute(&self) -> Vec { if self.action_state == ActionState::Completed { vec![] @@ -41,7 +40,7 @@ impl Actionable for StartSystemdUnit { #[tracing::instrument(skip_all, fields( unit = %self.unit, ))] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { unit, action_state } = self; if *action_state == ActionState::Completed { tracing::trace!("Already completed: Starting systemd unit"); @@ -57,7 +56,7 @@ impl Actionable for StartSystemdUnit { .arg(format!("{unit}")), ) .await - .map_err(StartSystemdUnitError::Command)?; + .map_err(|e| StartSystemdUnitError::Command(e).boxed())?; tracing::trace!("Started systemd unit"); *action_state = ActionState::Completed; @@ -80,7 +79,7 @@ impl Actionable for StartSystemdUnit { #[tracing::instrument(skip_all, fields( unit = %self.unit, ))] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { unit, action_state } = self; if *action_state == ActionState::Uncompleted { tracing::trace!("Already reverted: Stopping systemd unit"); @@ -91,7 +90,7 @@ impl Actionable for StartSystemdUnit { // TODO(@Hoverbear): Handle proxy vars execute_command(Command::new("systemctl").arg("stop").arg(format!("{unit}"))) .await - .map_err(StartSystemdUnitError::Command)?; + .map_err(|e| StartSystemdUnitError::Command(e).boxed())?; tracing::trace!("Stopped systemd unit"); *action_state = ActionState::Completed; @@ -99,12 +98,6 @@ impl Actionable for StartSystemdUnit { } } -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")] diff --git a/src/actions/base/systemd_sysext_merge.rs b/src/actions/base/systemd_sysext_merge.rs index d710189..f551ff8 100644 --- a/src/actions/base/systemd_sysext_merge.rs +++ b/src/actions/base/systemd_sysext_merge.rs @@ -5,7 +5,7 @@ use tokio::process::Command; use crate::execute_command; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct SystemdSysextMerge { @@ -24,9 +24,8 @@ impl SystemdSysextMerge { } #[async_trait::async_trait] +#[typetag::serde(name = "systemd-sysext-merge")] impl Actionable for SystemdSysextMerge { - type Error = SystemdSysextMergeError; - fn describe_execute(&self) -> Vec { let Self { action_state, @@ -45,7 +44,7 @@ impl Actionable for SystemdSysextMerge { #[tracing::instrument(skip_all, fields( device = %self.device.display(), ))] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { device, action_state, @@ -58,7 +57,7 @@ impl Actionable for SystemdSysextMerge { execute_command(Command::new("systemd-sysext").arg("merge").arg(device)) .await - .map_err(Self::Error::Command)?; + .map_err(|e| SystemdSysextMergeError::Command(e).boxed())?; tracing::trace!("Merged systemd-sysext"); *action_state = ActionState::Completed; @@ -81,7 +80,7 @@ impl Actionable for SystemdSysextMerge { #[tracing::instrument(skip_all, fields( device = %self.device.display(), ))] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { device, action_state, @@ -95,7 +94,7 @@ impl Actionable for SystemdSysextMerge { // TODO(@Hoverbear): Handle proxy vars execute_command(Command::new("systemd-sysext").arg("unmerge").arg(device)) .await - .map_err(SystemdSysextMergeError::Command)?; + .map_err(|e| SystemdSysextMergeError::Command(e).boxed())?; tracing::trace!("Unmerged systemd-sysext"); *action_state = ActionState::Completed; @@ -103,12 +102,6 @@ impl Actionable for SystemdSysextMerge { } } -impl From for Action { - fn from(v: SystemdSysextMerge) -> Self { - Action::SystemdSysextMerge(v) - } -} - #[derive(Debug, thiserror::Error, Serialize)] pub enum SystemdSysextMergeError { #[error("Failed to execute command")] diff --git a/src/actions/meta/configure_nix.rs b/src/actions/meta/configure_nix.rs index 11daaad..9e7838d 100644 --- a/src/actions/meta/configure_nix.rs +++ b/src/actions/meta/configure_nix.rs @@ -12,12 +12,12 @@ use crate::{ ConfigureShellProfile, ConfigureShellProfileError, PlaceChannelConfiguration, PlaceChannelConfigurationError, PlaceNixConfiguration, PlaceNixConfigurationError, }, - Action, ActionState, + ActionState, }, cli::arg::ChannelValue, }; -use crate::actions::{ActionDescription, Actionable}; +use crate::actions::{ActionDescription, ActionError, Actionable}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct ConfigureNix { @@ -31,7 +31,9 @@ pub struct ConfigureNix { impl ConfigureNix { #[tracing::instrument(skip_all)] - pub async fn plan(settings: CommonSettings) -> Result { + pub async fn plan( + settings: CommonSettings, + ) -> Result> { let channels: Vec<(String, Url)> = settings .channels .iter() @@ -39,7 +41,9 @@ impl ConfigureNix { .collect(); let setup_default_profile = - SetupDefaultProfile::plan(channels.iter().map(|(v, _k)| v.clone()).collect()).await?; + SetupDefaultProfile::plan(channels.iter().map(|(v, _k)| v.clone()).collect()) + .await + .map_err(|e| e.boxed())?; let configure_shell_profile = if settings.modify_profile { Some(ConfigureShellProfile::plan().await?) @@ -68,8 +72,8 @@ impl ConfigureNix { } #[async_trait::async_trait] +#[typetag::serde(name = "configure-nix")] impl Actionable for ConfigureNix { - type Error = ConfigureNixError; fn describe_execute(&self) -> Vec { let Self { setup_default_profile, @@ -95,7 +99,7 @@ impl Actionable for ConfigureNix { } #[tracing::instrument(skip_all)] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { setup_default_profile, configure_nix_daemon_service, @@ -113,51 +117,16 @@ impl Actionable for ConfigureNix { if let Some(configure_shell_profile) = configure_shell_profile { tokio::try_join!( - 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)) - }, + async move { setup_default_profile.execute().await }, + async move { place_nix_configuration.execute().await }, + async move { place_channel_configuration.execute().await }, + async move { configure_shell_profile.execute().await }, )?; } else { tokio::try_join!( - 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 { setup_default_profile.execute().await }, + async move { place_nix_configuration.execute().await }, + async move { place_channel_configuration.execute().await }, )?; }; configure_nix_daemon_service.execute().await?; @@ -194,7 +163,7 @@ impl Actionable for ConfigureNix { } #[tracing::instrument(skip_all)] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { setup_default_profile, configure_nix_daemon_service, @@ -223,43 +192,3 @@ impl Actionable for ConfigureNix { Ok(()) } } - -impl From for Action { - fn from(v: ConfigureNix) -> Self { - Action::ConfigureNix(v) - } -} - -#[derive(Debug, thiserror::Error, Serialize)] -pub enum ConfigureNixError { - #[error("Setting up default profile")] - SetupDefaultProfile( - #[source] - #[from] - SetupDefaultProfileError, - ), - #[error("Placing Nix configuration")] - PlaceNixConfiguration( - #[source] - #[from] - PlaceNixConfigurationError, - ), - #[error("Placing channel configuration")] - PlaceChannelConfiguration( - #[source] - #[from] - PlaceChannelConfigurationError, - ), - #[error("Configuring Nix daemon")] - ConfigureNixDaemonService( - #[source] - #[from] - ConfigureNixDaemonServiceError, - ), - #[error("Configuring shell profile")] - ConfigureShellProfile( - #[source] - #[from] - ConfigureShellProfileError, - ), -} diff --git a/src/actions/meta/configure_shell_profile.rs b/src/actions/meta/configure_shell_profile.rs index 74e0627..1e8e079 100644 --- a/src/actions/meta/configure_shell_profile.rs +++ b/src/actions/meta/configure_shell_profile.rs @@ -4,7 +4,7 @@ use serde::Serialize; use tokio::task::{JoinError, JoinSet}; use crate::actions::base::{CreateOrAppendFile, CreateOrAppendFileError}; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; const PROFILE_TARGETS: &[&str] = &[ "/etc/bashrc", @@ -24,7 +24,7 @@ pub struct ConfigureShellProfile { impl ConfigureShellProfile { #[tracing::instrument(skip_all)] - pub async fn plan() -> Result { + 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); @@ -41,8 +41,11 @@ impl ConfigureShellProfile { # End Nix\n \n", ); - create_or_append_files - .push(CreateOrAppendFile::plan(path, None, None, 0o0644, buf).await?); + create_or_append_files.push( + CreateOrAppendFile::plan(path, None, None, 0o0644, buf) + .await + .map_err(|e| e.boxed())?, + ); } Ok(Self { @@ -53,9 +56,8 @@ impl ConfigureShellProfile { } #[async_trait::async_trait] +#[typetag::serde(name = "configure-shell-profile")] impl Actionable for ConfigureShellProfile { - type Error = ConfigureShellProfileError; - fn describe_execute(&self) -> Vec { if self.action_state == ActionState::Completed { vec![] @@ -68,7 +70,7 @@ impl Actionable for ConfigureShellProfile { } #[tracing::instrument(skip_all)] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { create_or_append_files, action_state, @@ -87,7 +89,10 @@ impl Actionable for ConfigureShellProfile { 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)) + Result::<_, Box>::Ok(( + idx, + create_or_append_file_clone, + )) }); } @@ -97,7 +102,7 @@ impl Actionable for ConfigureShellProfile { create_or_append_files[idx] = create_or_append_file }, Ok(Err(e)) => errors.push(e), - Err(e) => return Err(e.into()), + Err(e) => return Err(e.boxed()), }; } @@ -105,9 +110,7 @@ impl Actionable for ConfigureShellProfile { if errors.len() == 1 { return Err(errors.into_iter().next().unwrap().into()); } else { - return Err(ConfigureShellProfileError::MultipleCreateOrAppendFile( - errors, - )); + return Err(ConfigureShellProfileError::MultipleCreateOrAppendFile(errors).boxed()); } } @@ -128,7 +131,7 @@ impl Actionable for ConfigureShellProfile { } #[tracing::instrument(skip_all)] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { create_or_append_files, action_state, @@ -147,7 +150,10 @@ impl Actionable for ConfigureShellProfile { let mut create_or_append_file_clone = create_or_append_file.clone(); let _abort_handle = set.spawn(async move { create_or_append_file_clone.revert().await?; - Result::<_, CreateOrAppendFileError>::Ok((idx, create_or_append_file_clone)) + Result::<_, Box>::Ok(( + idx, + create_or_append_file_clone, + )) }); } @@ -157,7 +163,7 @@ impl Actionable for ConfigureShellProfile { create_or_append_files[idx] = create_or_append_file }, Ok(Err(e)) => errors.push(e), - Err(e) => return Err(e.into()), + Err(e) => return Err(e.boxed()), }; } @@ -165,9 +171,7 @@ impl Actionable for ConfigureShellProfile { if errors.len() == 1 { return Err(errors.into_iter().next().unwrap().into()); } else { - return Err(ConfigureShellProfileError::MultipleCreateOrAppendFile( - errors, - )); + return Err(ConfigureShellProfileError::MultipleCreateOrAppendFile(errors).boxed()); } } @@ -177,13 +181,7 @@ impl Actionable for ConfigureShellProfile { } } -impl From for Action { - fn from(v: ConfigureShellProfile) -> Self { - Action::ConfigureShellProfile(v) - } -} - -#[derive(Debug, thiserror::Error, Serialize)] +#[derive(Debug, thiserror::Error)] pub enum ConfigureShellProfileError { #[error("Creating or appending to file")] CreateOrAppendFile( @@ -192,12 +190,11 @@ pub enum ConfigureShellProfileError { CreateOrAppendFileError, ), #[error("Multiple errors: {}", .0.iter().map(|v| format!("{v}")).collect::>().join(" & "))] - MultipleCreateOrAppendFile(Vec), + MultipleCreateOrAppendFile(Vec>), #[error("Joining spawned async task")] Join( #[source] #[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 bbae380..feb151c 100644 --- a/src/actions/meta/create_nix_tree.rs +++ b/src/actions/meta/create_nix_tree.rs @@ -1,7 +1,7 @@ use serde::Serialize; use crate::actions::base::{CreateDirectory, CreateDirectoryError}; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; const PATHS: &[&str] = &[ "/nix/var", @@ -27,7 +27,7 @@ pub struct CreateNixTree { impl CreateNixTree { #[tracing::instrument(skip_all)] - pub async fn plan() -> Result { + pub async fn plan() -> 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 @@ -42,9 +42,8 @@ impl CreateNixTree { } #[async_trait::async_trait] +#[typetag::serde(name = "creat-nix-tree")] impl Actionable for CreateNixTree { - type Error = CreateNixTreeError; - fn describe_execute(&self) -> Vec { if self.action_state == ActionState::Completed { vec![] @@ -69,7 +68,7 @@ impl Actionable for CreateNixTree { } #[tracing::instrument(skip_all)] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { create_directories, action_state, @@ -116,7 +115,7 @@ impl Actionable for CreateNixTree { } #[tracing::instrument(skip_all)] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { create_directories, action_state, @@ -139,12 +138,6 @@ impl Actionable for CreateNixTree { } } -impl From for Action { - fn from(v: CreateNixTree) -> Self { - Action::CreateNixTree(v) - } -} - #[derive(Debug, thiserror::Error, Serialize)] pub enum CreateNixTreeError { #[error("Creating directory")] diff --git a/src/actions/meta/create_systemd_sysext.rs b/src/actions/meta/create_systemd_sysext.rs index 44c47b8..c2cc20b 100644 --- a/src/actions/meta/create_systemd_sysext.rs +++ b/src/actions/meta/create_systemd_sysext.rs @@ -2,7 +2,7 @@ use serde::Serialize; use std::path::{Path, PathBuf}; use crate::actions::base::{CreateDirectory, CreateDirectoryError, CreateFile, CreateFileError}; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; const PATHS: &[&str] = &[ "usr", @@ -23,7 +23,9 @@ pub struct CreateSystemdSysext { impl CreateSystemdSysext { #[tracing::instrument(skip_all)] - pub async fn plan(destination: impl AsRef) -> Result { + pub async fn plan( + destination: impl AsRef, + ) -> Result> { let destination = destination.as_ref(); let mut create_directories = @@ -43,8 +45,8 @@ impl CreateSystemdSysext { false => None, }) }) - .map_err(CreateSystemdSysextError::ReadingOsRelease)? - .ok_or(CreateSystemdSysextError::NoVersionId)?; + .map_err(|e| CreateSystemdSysextError::ReadingOsRelease(e).boxed())? + .ok_or_else(|| CreateSystemdSysextError::NoVersionId.boxed())?; let extension_release_buf = format!("SYSEXT_LEVEL=1.0\nID=steamos\nVERSION_ID={version_id}"); let create_extension_release = CreateFile::plan( @@ -88,9 +90,8 @@ impl CreateSystemdSysext { } #[async_trait::async_trait] +#[typetag::serde(name = "create-systemd-sysext")] impl Actionable for CreateSystemdSysext { - type Error = CreateSystemdSysextError; - fn describe_execute(&self) -> Vec { let Self { action_state: _, @@ -112,7 +113,7 @@ impl Actionable for CreateSystemdSysext { } #[tracing::instrument(skip_all, fields(destination,))] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { destination: _, action_state, @@ -156,7 +157,7 @@ impl Actionable for CreateSystemdSysext { } #[tracing::instrument(skip_all, fields(destination,))] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { destination: _, action_state, @@ -184,12 +185,6 @@ impl Actionable for CreateSystemdSysext { } } -impl From for Action { - fn from(v: CreateSystemdSysext) -> Self { - Action::CreateSystemdSysext(v) - } -} - #[derive(Debug, thiserror::Error, Serialize)] pub enum CreateSystemdSysextError { #[error(transparent)] diff --git a/src/actions/meta/create_users_and_group.rs b/src/actions/meta/create_users_and_group.rs index 3e720de..18ece20 100644 --- a/src/actions/meta/create_users_and_group.rs +++ b/src/actions/meta/create_users_and_group.rs @@ -4,7 +4,7 @@ use tokio::task::{JoinError, JoinSet}; use crate::CommonSettings; use crate::actions::base::{CreateGroup, CreateGroupError, CreateUserError}; -use crate::actions::{Action, ActionDescription, ActionState, Actionable, CreateUser}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable, CreateUser}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct CreateUsersAndGroup { @@ -51,9 +51,8 @@ impl CreateUsersAndGroup { } #[async_trait::async_trait] +#[typetag::serde(name = "create-users-and-group")] impl Actionable for CreateUsersAndGroup { - type Error = CreateUsersAndGroupError; - fn describe_execute(&self) -> Vec { let Self { daemon_user_count, @@ -88,7 +87,7 @@ impl Actionable for CreateUsersAndGroup { nix_build_user_prefix = self.nix_build_user_prefix, nix_build_user_id_base = self.nix_build_user_id_base, ))] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { create_users, create_group, @@ -173,7 +172,7 @@ impl Actionable for CreateUsersAndGroup { nix_build_user_prefix = self.nix_build_user_prefix, nix_build_user_id_base = self.nix_build_user_id_base, ))] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { create_users, create_group, @@ -199,7 +198,7 @@ impl Actionable for CreateUsersAndGroup { let mut create_user_clone = create_user.clone(); let _abort_handle = set.spawn(async move { create_user_clone.revert().await?; - Result::<_, CreateUserError>::Ok((idx, create_user_clone)) + Result::<_, Box>::Ok((idx, create_user_clone)) }); } @@ -207,7 +206,7 @@ impl Actionable for CreateUsersAndGroup { match result { Ok(Ok((idx, success))) => create_users[idx] = success, Ok(Err(e)) => errors.push(e), - Err(e) => return Err(e)?, + Err(e) => return Err(e.boxed())?, }; } @@ -215,7 +214,7 @@ impl Actionable for CreateUsersAndGroup { if errors.len() == 1 { return Err(errors.into_iter().next().unwrap().into()); } else { - return Err(CreateUsersAndGroupError::CreateUsers(errors)); + return Err(CreateUsersAndGroupError::CreateUsers(errors).boxed()); } } @@ -228,13 +227,7 @@ impl Actionable for CreateUsersAndGroup { } } -impl From for Action { - fn from(v: CreateUsersAndGroup) -> Self { - Action::CreateUsersAndGroup(v) - } -} - -#[derive(Debug, thiserror::Error, Serialize)] +#[derive(Debug, thiserror::Error)] pub enum CreateUsersAndGroupError { #[error("Creating user")] CreateUser( @@ -243,7 +236,7 @@ pub enum CreateUsersAndGroupError { CreateUserError, ), #[error("Multiple errors: {}", .0.iter().map(|v| format!("{v}")).collect::>().join(" & "))] - CreateUsers(Vec), + CreateUsers(Vec>), #[error("Creating group")] CreateGroup( #[source] @@ -254,7 +247,6 @@ pub enum CreateUsersAndGroupError { Join( #[source] #[from] - #[serde(serialize_with = "crate::serialize_error_to_display")] JoinError, ), } diff --git a/src/actions/meta/darwin/create_apfs_volume.rs b/src/actions/meta/darwin/create_apfs_volume.rs index 547e098..2cf3fdf 100644 --- a/src/actions/meta/darwin/create_apfs_volume.rs +++ b/src/actions/meta/darwin/create_apfs_volume.rs @@ -5,15 +5,19 @@ use std::{ }; use tokio::process::Command; -use crate::actions::base::{ - darwin::{ - BootstrapVolume, BootstrapVolumeError, CreateSyntheticObjects, CreateSyntheticObjectsError, - CreateVolume, CreateVolumeError, EnableOwnership, EnableOwnershipError, EncryptVolume, - EncryptVolumeError, UnmountVolume, UnmountVolumeError, +use crate::actions::{ + base::{ + darwin::{ + BootstrapVolume, BootstrapVolumeError, CreateSyntheticObjects, + CreateSyntheticObjectsError, CreateVolume, CreateVolumeError, EnableOwnership, + EnableOwnershipError, EncryptVolume, EncryptVolumeError, UnmountVolume, + UnmountVolumeError, + }, + CreateFile, CreateFileError, CreateOrAppendFile, CreateOrAppendFileError, }, - CreateFile, CreateFileError, CreateOrAppendFile, CreateOrAppendFileError, + ActionError, }; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionState, Actionable}; const NIX_VOLUME_MOUNTD_DEST: &str = "/Library/LaunchDaemons/org.nixos.darwin-store.plist"; @@ -42,7 +46,7 @@ impl CreateApfsVolume { name: String, case_sensitive: bool, encrypt: Option, - ) -> Result { + ) -> Result> { let disk = disk.as_ref(); let create_or_append_synthetic_conf = CreateOrAppendFile::plan( "/etc/synthetic.conf", @@ -51,7 +55,8 @@ impl CreateApfsVolume { 0o0655, "nix\n".into(), /* The newline is required otherwise it segfaults */ ) - .await?; + .await + .map_err(|e| e.boxed())?; let create_synthetic_objects = CreateSyntheticObjects::plan().await?; @@ -66,7 +71,8 @@ impl CreateApfsVolume { 0o0655, format!("NAME=\"{name}\" /nix apfs rw,noauto,nobrowse,suid,owners"), ) - .await?; + .await + .map_err(|e| e.boxed())?; let encrypt_volume = if let Some(password) = encrypt.as_ref() { Some(EncryptVolume::plan(disk, password.to_string()).await?) @@ -140,9 +146,8 @@ impl CreateApfsVolume { } #[async_trait::async_trait] +#[typetag::serde(name = "create-apfs-volume")] impl Actionable for CreateApfsVolume { - type Error = CreateApfsVolumeError; - fn describe_execute(&self) -> Vec { let Self { disk, @@ -163,7 +168,7 @@ impl Actionable for CreateApfsVolume { } #[tracing::instrument(skip_all, fields(destination,))] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { disk: _, name: _, @@ -207,7 +212,7 @@ impl Actionable for CreateApfsVolume { .stdout(std::process::Stdio::null()) .status() .await - .map_err(Self::Error::Command)?; + .map_err(|e| CreateApfsVolumeError::Command(e).boxed())?; if status.success() || retry_tokens == 0 { break; } else { @@ -243,7 +248,7 @@ impl Actionable for CreateApfsVolume { } #[tracing::instrument(skip_all, fields(disk, name))] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { disk: _, name: _, @@ -287,12 +292,6 @@ impl Actionable for CreateApfsVolume { } } -impl From for Action { - fn from(v: CreateApfsVolume) -> Self { - Action::CreateApfsVolume(v) - } -} - #[derive(Debug, thiserror::Error, Serialize)] pub enum CreateApfsVolumeError { #[error(transparent)] diff --git a/src/actions/meta/mod.rs b/src/actions/meta/mod.rs index e153b8e..2288eaa 100644 --- a/src/actions/meta/mod.rs +++ b/src/actions/meta/mod.rs @@ -10,7 +10,7 @@ mod place_channel_configuration; mod place_nix_configuration; mod provision_nix; -pub use configure_nix::{ConfigureNix, ConfigureNixError}; +pub use configure_nix::ConfigureNix; pub use configure_shell_profile::{ConfigureShellProfile, ConfigureShellProfileError}; pub use create_nix_tree::{CreateNixTree, CreateNixTreeError}; pub use create_systemd_sysext::{CreateSystemdSysext, CreateSystemdSysextError}; diff --git a/src/actions/meta/place_channel_configuration.rs b/src/actions/meta/place_channel_configuration.rs index 56a34cc..825213c 100644 --- a/src/actions/meta/place_channel_configuration.rs +++ b/src/actions/meta/place_channel_configuration.rs @@ -1,7 +1,7 @@ use reqwest::Url; use serde::Serialize; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; use crate::actions::base::{CreateFile, CreateFileError}; @@ -17,7 +17,7 @@ impl PlaceChannelConfiguration { pub async fn plan( channels: Vec<(String, Url)>, force: bool, - ) -> Result { + ) -> Result> { let buf = channels .iter() .map(|(name, url)| format!("{} {}", url, name)) @@ -25,7 +25,7 @@ impl PlaceChannelConfiguration { .join("\n"); let create_file = CreateFile::plan( dirs::home_dir() - .ok_or(PlaceChannelConfigurationError::NoRootHome)? + .ok_or_else(|| PlaceChannelConfigurationError::NoRootHome.boxed())? .join(".nix-channels"), None, None, @@ -43,9 +43,8 @@ impl PlaceChannelConfiguration { } #[async_trait::async_trait] +#[typetag::serde(name = "place-channel-configuration")] impl Actionable for PlaceChannelConfiguration { - type Error = PlaceChannelConfigurationError; - fn describe_execute(&self) -> Vec { let Self { channels: _, @@ -68,7 +67,7 @@ impl Actionable for PlaceChannelConfiguration { #[tracing::instrument(skip_all, fields( channels = self.channels.iter().map(|(c, u)| format!("{c}={u}")).collect::>().join(", "), ))] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { create_file, channels: _, @@ -110,7 +109,7 @@ impl Actionable for PlaceChannelConfiguration { #[tracing::instrument(skip_all, fields( channels = self.channels.iter().map(|(c, u)| format!("{c}={u}")).collect::>().join(", "), ))] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { create_file, channels: _, @@ -131,12 +130,6 @@ impl Actionable for PlaceChannelConfiguration { } } -impl From for Action { - fn from(v: PlaceChannelConfiguration) -> Self { - Action::PlaceChannelConfiguration(v) - } -} - #[derive(Debug, thiserror::Error, Serialize)] pub enum PlaceChannelConfigurationError { #[error("Creating file")] diff --git a/src/actions/meta/place_nix_configuration.rs b/src/actions/meta/place_nix_configuration.rs index 80402a1..b0199d2 100644 --- a/src/actions/meta/place_nix_configuration.rs +++ b/src/actions/meta/place_nix_configuration.rs @@ -1,6 +1,6 @@ use serde::Serialize; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; use crate::actions::base::{CreateDirectory, CreateDirectoryError, CreateFile, CreateFileError}; @@ -20,7 +20,7 @@ impl PlaceNixConfiguration { nix_build_group_name: String, extra_conf: Option, force: bool, - ) -> Result { + ) -> Result> { let buf = format!( "\ {extra_conf}\n\ @@ -44,9 +44,8 @@ impl PlaceNixConfiguration { } #[async_trait::async_trait] +#[typetag::serde(name = "place-nix-configuration")] impl Actionable for PlaceNixConfiguration { - type Error = PlaceNixConfigurationError; - fn describe_execute(&self) -> Vec { if self.action_state == ActionState::Completed { vec![] @@ -62,7 +61,7 @@ impl Actionable for PlaceNixConfiguration { } #[tracing::instrument(skip_all)] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { create_file, create_directory, @@ -98,7 +97,7 @@ impl Actionable for PlaceNixConfiguration { } #[tracing::instrument(skip_all)] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { create_file, create_directory, @@ -120,12 +119,6 @@ impl Actionable for PlaceNixConfiguration { } } -impl From for Action { - fn from(v: PlaceNixConfiguration) -> Self { - Action::PlaceNixConfiguration(v) - } -} - #[derive(Debug, thiserror::Error, Serialize)] pub enum PlaceNixConfigurationError { #[error("Creating file")] diff --git a/src/actions/meta/provision_nix.rs b/src/actions/meta/provision_nix.rs index 21d0fd1..1450f9b 100644 --- a/src/actions/meta/provision_nix.rs +++ b/src/actions/meta/provision_nix.rs @@ -8,7 +8,7 @@ use crate::actions::base::{ }; use crate::CommonSettings; -use crate::actions::{Action, ActionDescription, ActionState, Actionable}; +use crate::actions::{ActionDescription, ActionError, ActionState, Actionable}; use super::{CreateNixTree, CreateNixTreeError, CreateUsersAndGroup, CreateUsersAndGroupError}; @@ -23,16 +23,22 @@ pub struct ProvisionNix { impl ProvisionNix { #[tracing::instrument(skip_all)] - pub async fn plan(settings: CommonSettings) -> Result { + pub async fn plan( + settings: CommonSettings, + ) -> Result> { let fetch_nix = FetchNix::plan( settings.nix_package_url.clone(), PathBuf::from("/nix/temp-install-dir"), ) - .await?; - let create_users_and_group = CreateUsersAndGroup::plan(settings.clone()).await?; + .await + .map_err(|e| e.boxed())?; + let create_users_and_group = CreateUsersAndGroup::plan(settings.clone()) + .await + .map_err(|e| e.boxed())?; let create_nix_tree = CreateNixTree::plan().await?; - let move_unpacked_nix = - MoveUnpackedNix::plan(PathBuf::from("/nix/temp-install-dir")).await?; + let move_unpacked_nix = MoveUnpackedNix::plan(PathBuf::from("/nix/temp-install-dir")) + .await + .map_err(|e| e.boxed())?; Ok(Self { fetch_nix, create_users_and_group, @@ -44,8 +50,8 @@ impl ProvisionNix { } #[async_trait::async_trait] +#[typetag::serde(name = "provision-nix")] impl Actionable for ProvisionNix { - type Error = ProvisionNixError; fn describe_execute(&self) -> Vec { let Self { fetch_nix, @@ -68,7 +74,7 @@ impl Actionable for ProvisionNix { } #[tracing::instrument(skip_all)] - async fn execute(&mut self) -> Result<(), Self::Error> { + async fn execute(&mut self) -> Result<(), Box> { let Self { fetch_nix, create_nix_tree, @@ -87,16 +93,13 @@ impl Actionable for ProvisionNix { let mut fetch_nix_clone = fetch_nix.clone(); let fetch_nix_handle = tokio::task::spawn(async { fetch_nix_clone.execute().await?; - Result::<_, Self::Error>::Ok(fetch_nix_clone) + Result::<_, Box>::Ok(fetch_nix_clone) }); create_users_and_group.execute().await?; - create_nix_tree - .execute() - .await - .map_err(ProvisionNixError::from)?; + create_nix_tree.execute().await?; - *fetch_nix = fetch_nix_handle.await.map_err(ProvisionNixError::from)??; + *fetch_nix = fetch_nix_handle.await.map_err(|e| e.boxed())??; move_unpacked_nix.execute().await?; tracing::trace!("Provisioned Nix"); @@ -125,7 +128,7 @@ impl Actionable for ProvisionNix { } #[tracing::instrument(skip_all)] - async fn revert(&mut self) -> Result<(), Self::Error> { + async fn revert(&mut self) -> Result<(), Box> { let Self { fetch_nix, create_nix_tree, @@ -144,19 +147,19 @@ impl Actionable for ProvisionNix { let mut fetch_nix_clone = fetch_nix.clone(); let fetch_nix_handle = tokio::task::spawn(async { fetch_nix_clone.revert().await?; - Result::<_, Self::Error>::Ok(fetch_nix_clone) + Result::<_, Box>::Ok(fetch_nix_clone) }); if let Err(err) = create_users_and_group.revert().await { fetch_nix_handle.abort(); - return Err(Self::Error::from(err)); + return Err(err); } if let Err(err) = create_nix_tree.revert().await { fetch_nix_handle.abort(); - return Err(Self::Error::from(err)); + return Err(err); } - *fetch_nix = fetch_nix_handle.await.map_err(ProvisionNixError::from)??; + *fetch_nix = fetch_nix_handle.await.map_err(|e| e.boxed())??; move_unpacked_nix.revert().await?; tracing::trace!("Unprovisioned Nix"); @@ -165,13 +168,7 @@ impl Actionable for ProvisionNix { } } -impl From for Action { - fn from(v: ProvisionNix) -> Self { - Action::ProvisionNix(v) - } -} - -#[derive(Debug, thiserror::Error, Serialize)] +#[derive(Debug, thiserror::Error)] pub enum ProvisionNixError { #[error("Fetching Nix")] FetchNix( @@ -183,7 +180,6 @@ pub enum ProvisionNixError { Join( #[source] #[from] - #[serde(serialize_with = "crate::serialize_error_to_display")] JoinError, ), #[error("Creating directory")] diff --git a/src/actions/mod.rs b/src/actions/mod.rs index f117f5d..f34b4d1 100644 --- a/src/actions/mod.rs +++ b/src/actions/mod.rs @@ -11,26 +11,37 @@ use base::{ }; use meta::{ darwin::{CreateApfsVolume, CreateApfsVolumeError}, - ConfigureNix, ConfigureNixError, ConfigureShellProfile, ConfigureShellProfileError, - CreateNixTree, CreateNixTreeError, CreateSystemdSysext, CreateSystemdSysextError, - CreateUsersAndGroup, CreateUsersAndGroupError, PlaceChannelConfiguration, - PlaceChannelConfigurationError, PlaceNixConfiguration, PlaceNixConfigurationError, - ProvisionNix, ProvisionNixError, + ConfigureNix, ConfigureShellProfile, ConfigureShellProfileError, CreateNixTree, + CreateNixTreeError, CreateSystemdSysext, CreateSystemdSysextError, CreateUsersAndGroup, + CreateUsersAndGroupError, PlaceChannelConfiguration, PlaceChannelConfigurationError, + PlaceNixConfiguration, PlaceNixConfigurationError, ProvisionNix, ProvisionNixError, }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -#[async_trait::async_trait] -pub trait Actionable: DeserializeOwned + Serialize + Into { - type Error: std::error::Error + std::fmt::Debug + Serialize + Into; +pub trait ActionError: std::error::Error + Send + Sync { + fn boxed(self) -> Box + where + Self: Sized + 'static, + { + Box::new(self) + } +} +impl ActionError for E where E: std::error::Error + Send + Sized + Sync {} + +#[async_trait::async_trait] +#[typetag::serde(tag = "action")] +pub trait Actionable: Send + Sync + std::fmt::Debug + dyn_clone::DynClone { fn describe_execute(&self) -> Vec; fn describe_revert(&self) -> Vec; // They should also have an `async fn plan(args...) -> Result, Self::Error>;` - async fn execute(&mut self) -> Result<(), Self::Error>; - async fn revert(&mut self) -> Result<(), Self::Error>; + async fn execute(&mut self) -> Result<(), Box>; + async fn revert(&mut self) -> Result<(), Box>; } +dyn_clone::clone_trait_object!(Actionable); + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] pub enum ActionState { Completed, @@ -54,232 +65,3 @@ impl ActionDescription { } } } - -#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] -pub enum Action { - DarwinBootstrapVolume(base::darwin::BootstrapVolume), - DarwinCreateSyntheticObjects(base::darwin::CreateSyntheticObjects), - DarwinCreateVolume(base::darwin::CreateVolume), - DarwinEnableOwnership(base::darwin::EnableOwnership), - DarwinEncryptVolume(base::darwin::EncryptVolume), - DarwinKickStartLaunchctlService(base::darwin::KickstartLaunchctlService), - DarwinUnmountVolume(base::darwin::UnmountVolume), - ConfigureNix(ConfigureNix), - ConfigureNixDaemonService(ConfigureNixDaemonService), - ConfigureShellProfile(ConfigureShellProfile), - CreateApfsVolume(CreateApfsVolume), - CreateDirectory(CreateDirectory), - CreateFile(CreateFile), - CreateGroup(CreateGroup), - CreateNixTree(CreateNixTree), - CreateOrAppendFile(CreateOrAppendFile), - CreateSystemdSysext(CreateSystemdSysext), - CreateUser(CreateUser), - CreateUsersAndGroup(CreateUsersAndGroup), - FetchNix(FetchNix), - MoveUnpackedNix(MoveUnpackedNix), - PlaceChannelConfiguration(PlaceChannelConfiguration), - PlaceNixConfiguration(PlaceNixConfiguration), - ProvisionNix(ProvisionNix), - SetupDefaultProfile(SetupDefaultProfile), - StartSystemdUnit(StartSystemdUnit), - SystemdSysextMerge(SystemdSysextMerge), -} - -#[derive(Debug, thiserror::Error, serde::Serialize)] -pub enum ActionError { - #[error(transparent)] - DarwinBootstrapVolume(#[from] base::darwin::BootstrapVolumeError), - #[error(transparent)] - DarwinCreateSyntheticObjects(#[from] base::darwin::CreateSyntheticObjectsError), - #[error(transparent)] - DarwinCreateVolume(#[from] base::darwin::CreateVolumeError), - #[error(transparent)] - DarwinEnableOwnership(#[from] base::darwin::EnableOwnershipError), - #[error(transparent)] - DarwinEncryptVolume(#[from] base::darwin::EncryptVolumeError), - #[error(transparent)] - DarwinKickStartLaunchctlService(#[from] base::darwin::KickstartLaunchctlServiceError), - #[error(transparent)] - DarwinUnmountVolume(#[from] base::darwin::UnmountVolumeError), - #[error("Attempted to revert an unexecuted action")] - NotExecuted(Action), - #[error("Attempted to execute an already executed action")] - AlreadyExecuted(Action), - #[error("Attempted to revert an already reverted action")] - AlreadyReverted(Action), - #[error(transparent)] - CreateApfsVolume(#[from] CreateApfsVolumeError), - #[error(transparent)] - ConfigureNixDaemonService(#[from] ConfigureNixDaemonServiceError), - #[error(transparent)] - ConfigureNix(#[from] ConfigureNixError), - #[error(transparent)] - ConfigureShellProfile(#[from] ConfigureShellProfileError), - #[error(transparent)] - CreateDirectory(#[from] CreateDirectoryError), - #[error(transparent)] - CreateSystemdSysext(#[from] CreateSystemdSysextError), - #[error(transparent)] - CreateFile(#[from] CreateFileError), - #[error(transparent)] - CreateGroup(#[from] CreateGroupError), - #[error(transparent)] - CreateOrAppendFile(#[from] CreateOrAppendFileError), - #[error(transparent)] - CreateNixTree(#[from] CreateNixTreeError), - #[error(transparent)] - CreateUser(#[from] CreateUserError), - #[error(transparent)] - CreateUsersAndGroup(#[from] CreateUsersAndGroupError), - #[error(transparent)] - FetchNix(#[from] FetchNixError), - #[error(transparent)] - MoveUnpackedNix(#[from] MoveUnpackedNixError), - #[error(transparent)] - PlaceChannelConfiguration(#[from] PlaceChannelConfigurationError), - #[error(transparent)] - PlaceNixConfiguration(#[from] PlaceNixConfigurationError), - #[error(transparent)] - SetupDefaultProfile(#[from] SetupDefaultProfileError), - #[error(transparent)] - StartSystemdUnit(#[from] StartSystemdUnitError), - #[error(transparent)] - SystemdSysExtMerge(#[from] SystemdSysextMergeError), - #[error(transparent)] - ProvisionNix(#[from] ProvisionNixError), -} - -#[async_trait::async_trait] -impl Actionable for Action { - type Error = ActionError; - fn describe_execute(&self) -> Vec { - match self { - Action::DarwinBootstrapVolume(i) => i.describe_execute(), - Action::DarwinCreateSyntheticObjects(i) => i.describe_execute(), - Action::DarwinCreateVolume(i) => i.describe_execute(), - Action::DarwinEnableOwnership(i) => i.describe_execute(), - Action::DarwinEncryptVolume(i) => i.describe_execute(), - Action::DarwinUnmountVolume(i) => i.describe_execute(), - Action::ConfigureNix(i) => i.describe_execute(), - Action::ConfigureNixDaemonService(i) => i.describe_execute(), - Action::ConfigureShellProfile(i) => i.describe_execute(), - Action::CreateApfsVolume(i) => i.describe_execute(), - Action::CreateDirectory(i) => i.describe_execute(), - Action::CreateFile(i) => i.describe_execute(), - Action::CreateGroup(i) => i.describe_execute(), - Action::CreateNixTree(i) => i.describe_execute(), - Action::CreateOrAppendFile(i) => i.describe_execute(), - Action::CreateSystemdSysext(i) => i.describe_execute(), - Action::CreateUser(i) => i.describe_execute(), - Action::CreateUsersAndGroup(i) => i.describe_execute(), - Action::FetchNix(i) => i.describe_execute(), - Action::MoveUnpackedNix(i) => i.describe_execute(), - Action::PlaceChannelConfiguration(i) => i.describe_execute(), - Action::PlaceNixConfiguration(i) => i.describe_execute(), - Action::ProvisionNix(i) => i.describe_execute(), - Action::SetupDefaultProfile(i) => i.describe_execute(), - Action::DarwinKickStartLaunchctlService(i) => i.describe_execute(), - Action::StartSystemdUnit(i) => i.describe_execute(), - Action::SystemdSysextMerge(i) => i.describe_execute(), - } - } - - async fn execute(&mut self) -> Result<(), Self::Error> { - match self { - Action::DarwinBootstrapVolume(i) => i.execute().await?, - Action::DarwinCreateSyntheticObjects(i) => i.execute().await?, - Action::DarwinCreateVolume(i) => i.execute().await?, - Action::DarwinEnableOwnership(i) => i.execute().await?, - Action::DarwinEncryptVolume(i) => i.execute().await?, - Action::DarwinUnmountVolume(i) => i.execute().await?, - Action::ConfigureNix(i) => i.execute().await?, - Action::ConfigureNixDaemonService(i) => i.execute().await?, - Action::ConfigureShellProfile(i) => i.execute().await?, - Action::CreateApfsVolume(i) => i.execute().await?, - Action::CreateDirectory(i) => i.execute().await?, - Action::CreateFile(i) => i.execute().await?, - Action::CreateGroup(i) => i.execute().await?, - Action::CreateNixTree(i) => i.execute().await?, - Action::CreateOrAppendFile(i) => i.execute().await?, - Action::CreateSystemdSysext(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::ProvisionNix(i) => i.execute().await?, - Action::SetupDefaultProfile(i) => i.execute().await?, - Action::DarwinKickStartLaunchctlService(i) => i.execute().await?, - Action::StartSystemdUnit(i) => i.execute().await?, - Action::SystemdSysextMerge(i) => i.execute().await?, - }; - Ok(()) - } - - fn describe_revert(&self) -> Vec { - match self { - Action::DarwinBootstrapVolume(i) => i.describe_revert(), - Action::DarwinCreateSyntheticObjects(i) => i.describe_revert(), - Action::DarwinCreateVolume(i) => i.describe_revert(), - Action::DarwinEnableOwnership(i) => i.describe_revert(), - Action::DarwinEncryptVolume(i) => i.describe_revert(), - Action::DarwinUnmountVolume(i) => i.describe_revert(), - Action::ConfigureNix(i) => i.describe_revert(), - Action::ConfigureNixDaemonService(i) => i.describe_revert(), - Action::ConfigureShellProfile(i) => i.describe_revert(), - Action::CreateApfsVolume(i) => i.describe_revert(), - Action::CreateDirectory(i) => i.describe_revert(), - Action::CreateFile(i) => i.describe_revert(), - Action::CreateGroup(i) => i.describe_revert(), - Action::CreateNixTree(i) => i.describe_revert(), - Action::CreateOrAppendFile(i) => i.describe_revert(), - Action::CreateSystemdSysext(i) => i.describe_revert(), - Action::CreateUser(i) => i.describe_revert(), - Action::CreateUsersAndGroup(i) => i.describe_revert(), - Action::FetchNix(i) => i.describe_revert(), - Action::MoveUnpackedNix(i) => i.describe_revert(), - Action::PlaceChannelConfiguration(i) => i.describe_revert(), - Action::PlaceNixConfiguration(i) => i.describe_revert(), - Action::ProvisionNix(i) => i.describe_revert(), - Action::SetupDefaultProfile(i) => i.describe_revert(), - Action::DarwinKickStartLaunchctlService(i) => i.describe_revert(), - Action::StartSystemdUnit(i) => i.describe_revert(), - Action::SystemdSysextMerge(i) => i.describe_revert(), - } - } - - async fn revert(&mut self) -> Result<(), Self::Error> { - match self { - Action::DarwinBootstrapVolume(i) => i.revert().await?, - Action::DarwinCreateSyntheticObjects(i) => i.revert().await?, - Action::DarwinCreateVolume(i) => i.revert().await?, - Action::DarwinEnableOwnership(i) => i.revert().await?, - Action::DarwinEncryptVolume(i) => i.revert().await?, - Action::DarwinUnmountVolume(i) => i.revert().await?, - Action::ConfigureNix(i) => i.revert().await?, - Action::ConfigureNixDaemonService(i) => i.revert().await?, - Action::ConfigureShellProfile(i) => i.revert().await?, - Action::CreateApfsVolume(i) => i.revert().await?, - Action::CreateDirectory(i) => i.revert().await?, - Action::CreateFile(i) => i.revert().await?, - Action::CreateGroup(i) => i.revert().await?, - Action::CreateNixTree(i) => i.revert().await?, - Action::CreateOrAppendFile(i) => i.revert().await?, - Action::CreateSystemdSysext(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::ProvisionNix(i) => i.revert().await?, - Action::SetupDefaultProfile(i) => i.revert().await?, - Action::DarwinKickStartLaunchctlService(i) => i.revert().await?, - Action::StartSystemdUnit(i) => i.revert().await?, - Action::SystemdSysextMerge(i) => i.revert().await?, - } - Ok(()) - } -} diff --git a/src/error.rs b/src/error.rs index b99c07e..ed6a605 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,14 +1,12 @@ use std::path::PathBuf; -use crate::actions::ActionError; - #[derive(thiserror::Error, Debug)] pub enum HarmonicError { #[error("Error executing action")] ActionError( #[source] #[from] - ActionError, + Box, ), #[error("Recording install receipt")] RecordingReceipt(PathBuf, #[source] std::io::Error), diff --git a/src/plan.rs b/src/plan.rs index 2ecc45b..1276d33 100644 --- a/src/plan.rs +++ b/src/plan.rs @@ -1,13 +1,13 @@ use std::path::PathBuf; use crate::{ - actions::{Action, ActionDescription, ActionError, Actionable}, + actions::{ActionDescription, Actionable}, BuiltinPlanner, HarmonicError, }; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct InstallPlan { - pub(crate) actions: Vec, + pub(crate) actions: Vec>, pub(crate) planner: BuiltinPlanner, } @@ -70,11 +70,12 @@ impl InstallPlan { if let Err(err) = write_receipt(self.clone()).await { tracing::error!("Error saving receipt: {:?}", err); } - return Err(ActionError::from(err).into()); + return Err(HarmonicError::ActionError(err)); } } - write_receipt(self.clone()).await + write_receipt(self.clone()).await?; + Ok(()) } #[tracing::instrument(skip_all)] @@ -134,7 +135,7 @@ impl InstallPlan { if let Err(err) = write_receipt(self.clone()).await { tracing::error!("Error saving receipt: {:?}", err); } - return Err(ActionError::from(err).into()); + return Err(HarmonicError::ActionError(err)); } } diff --git a/src/planner/darwin/multi.rs b/src/planner/darwin/multi.rs index b2e5f00..c2f6d57 100644 --- a/src/planner/darwin/multi.rs +++ b/src/planner/darwin/multi.rs @@ -7,11 +7,10 @@ use crate::{ actions::{ base::darwin::KickstartLaunchctlService, meta::{darwin::CreateApfsVolume, ConfigureNix, ProvisionNix}, - Action, ActionError, }, execute_command, os::darwin::DiskUtilOutput, - planner::{Plannable, PlannerError}, + planner::{BuiltinPlannerError, Plannable}, BuiltinPlanner, CommonSettings, InstallPlan, }; @@ -32,7 +31,7 @@ pub struct DarwinMulti { root_disk: Option, } -async fn default_root_disk() -> Result { +async fn default_root_disk() -> Result { let buf = execute_command(Command::new("/usr/sbin/diskutil").args(["info", "-plist", "/"])) .await .unwrap() @@ -46,8 +45,9 @@ async fn default_root_disk() -> Result { impl Plannable for DarwinMulti { const DISPLAY_STRING: &'static str = "Darwin Multi-User"; const SLUG: &'static str = "darwin-multi"; + type Error = BuiltinPlannerError; - async fn default() -> Result { + async fn default() -> Result { Ok(Self { settings: CommonSettings::default()?, root_disk: Some(default_root_disk().await?), @@ -56,7 +56,7 @@ impl Plannable for DarwinMulti { }) } - async fn plan(self) -> Result { + async fn plan(self) -> Result { let root_disk = { let buf = execute_command(Command::new("/usr/sbin/diskutil").args(["info", "-plist", "/"])) @@ -77,22 +77,12 @@ impl Plannable for DarwinMulti { // // setup_Synthetic -> create_synthetic_objects // Unmount -> create_volume -> Setup_fstab -> maybe encrypt_volume -> launchctl bootstrap -> launchctl kickstart -> await_volume -> maybe enableOwnership - CreateApfsVolume::plan(root_disk, volume_label, false, None) - .await - .map(Action::from) - .map_err(ActionError::from)?, - ProvisionNix::plan(self.settings.clone()) - .await - .map(Action::from) - .map_err(ActionError::from)?, - ConfigureNix::plan(self.settings) - .await - .map(Action::from) - .map_err(ActionError::from)?, - KickstartLaunchctlService::plan("system/org.nixos.nix-daemon".into()) - .await - .map(Action::from) - .map_err(ActionError::from)?, + Box::new(CreateApfsVolume::plan(root_disk, volume_label, false, None).await?), + Box::new(ProvisionNix::plan(self.settings.clone()).await?), + Box::new(ConfigureNix::plan(self.settings).await?), + Box::new( + KickstartLaunchctlService::plan("system/org.nixos.nix-daemon".into()).await?, + ), ], }) } diff --git a/src/planner/linux/multi.rs b/src/planner/linux/multi.rs index 2b5cdee..7793e08 100644 --- a/src/planner/linux/multi.rs +++ b/src/planner/linux/multi.rs @@ -2,9 +2,8 @@ use crate::{ actions::{ base::{CreateDirectory, StartSystemdUnit}, meta::{ConfigureNix, ProvisionNix}, - Action, ActionError, }, - planner::{Plannable, PlannerError}, + planner::{BuiltinPlannerError, Plannable}, BuiltinPlanner, CommonSettings, InstallPlan, }; @@ -18,33 +17,22 @@ pub struct LinuxMulti { impl Plannable for LinuxMulti { const DISPLAY_STRING: &'static str = "Linux Multi-User"; const SLUG: &'static str = "linux-multi"; + type Error = BuiltinPlannerError; - async fn default() -> Result { + async fn default() -> Result { Ok(Self { settings: CommonSettings::default()?, }) } - async fn plan(self) -> Result { + async fn plan(self) -> Result { Ok(InstallPlan { planner: self.clone().into(), actions: vec![ - CreateDirectory::plan("/nix", None, None, 0o0755, true) - .await - .map(Action::from) - .map_err(ActionError::from)?, - ProvisionNix::plan(self.settings.clone()) - .await - .map(Action::from) - .map_err(ActionError::from)?, - ConfigureNix::plan(self.settings) - .await - .map(Action::from) - .map_err(ActionError::from)?, - StartSystemdUnit::plan("nix-daemon.socket".into()) - .await - .map(Action::from) - .map_err(ActionError::from)?, + Box::new(CreateDirectory::plan("/nix", None, None, 0o0755, true).await?), + Box::new(ProvisionNix::plan(self.settings.clone()).await?), + Box::new(ConfigureNix::plan(self.settings).await?), + Box::new(StartSystemdUnit::plan("nix-daemon.socket".into()).await?), ], }) } diff --git a/src/planner/mod.rs b/src/planner/mod.rs index 8dcc5dc..8b165d8 100644 --- a/src/planner/mod.rs +++ b/src/planner/mod.rs @@ -12,7 +12,7 @@ pub enum BuiltinPlanner { } impl BuiltinPlanner { - pub async fn default() -> Result { + pub async fn default() -> Result { use target_lexicon::{Architecture, OperatingSystem}; match (Architecture::host(), OperatingSystem::host()) { (Architecture::X86_64, OperatingSystem::Linux) => { @@ -29,11 +29,13 @@ impl BuiltinPlanner { | (Architecture::Aarch64(_), OperatingSystem::Darwin) => { Ok(Self::DarwinMulti(darwin::DarwinMulti::default().await?)) }, - _ => Err(PlannerError::UnsupportedArchitecture(target_lexicon::HOST)), + _ => Err(BuiltinPlannerError::UnsupportedArchitecture( + target_lexicon::HOST, + )), } } - pub async fn plan(self) -> Result { + pub async fn plan(self) -> Result { match self { BuiltinPlanner::LinuxMulti(planner) => planner.plan().await, BuiltinPlanner::DarwinMulti(planner) => planner.plan().await, @@ -43,26 +45,27 @@ impl BuiltinPlanner { } #[async_trait::async_trait] -trait Plannable: Into +trait Plannable where Self: Sized, { const DISPLAY_STRING: &'static str; const SLUG: &'static str; + type Error: std::error::Error; - async fn default() -> Result; - async fn plan(self) -> Result; + async fn default() -> Result; + async fn plan(self) -> Result; } #[derive(thiserror::Error, Debug)] -pub enum PlannerError { +pub enum BuiltinPlannerError { #[error("Harmonic does not have a default planner for the `{0}` architecture right now, pass a specific archetype")] UnsupportedArchitecture(target_lexicon::Triple), #[error("Error executing action")] ActionError( #[source] #[from] - ActionError, + Box, ), #[error(transparent)] InstallSettings(#[from] InstallSettingsError), diff --git a/src/planner/specific/steam_deck.rs b/src/planner/specific/steam_deck.rs index 04d687c..72f329f 100644 --- a/src/planner/specific/steam_deck.rs +++ b/src/planner/specific/steam_deck.rs @@ -2,9 +2,8 @@ use crate::{ actions::{ base::{CreateDirectory, StartSystemdUnit}, meta::{CreateSystemdSysext, ProvisionNix}, - Action, ActionError, }, - planner::{Plannable, PlannerError}, + planner::{BuiltinPlannerError, Plannable}, BuiltinPlanner, CommonSettings, InstallPlan, }; @@ -18,33 +17,22 @@ pub struct SteamDeck { impl Plannable for SteamDeck { const DISPLAY_STRING: &'static str = "Steam Deck (x86_64 Linux Multi-User)"; const SLUG: &'static str = "steam-deck"; + type Error = BuiltinPlannerError; - async fn default() -> Result { + async fn default() -> Result { Ok(Self { settings: CommonSettings::default()?, }) } - async fn plan(self) -> Result { + async fn plan(self) -> Result { Ok(InstallPlan { planner: self.clone().into(), actions: vec![ - CreateSystemdSysext::plan("/var/lib/extensions") - .await - .map(Action::from) - .map_err(ActionError::from)?, - CreateDirectory::plan("/nix", None, None, 0o0755, true) - .await - .map(Action::from) - .map_err(ActionError::from)?, - ProvisionNix::plan(self.settings.clone()) - .await - .map(Action::from) - .map_err(ActionError::from)?, - StartSystemdUnit::plan("nix-daemon.socket".into()) - .await - .map(Action::from) - .map_err(ActionError::from)?, + Box::new(CreateSystemdSysext::plan("/var/lib/extensions").await?), + Box::new(CreateDirectory::plan("/nix", None, None, 0o0755, true).await?), + Box::new(ProvisionNix::plan(self.settings.clone()).await?), + Box::new(StartSystemdUnit::plan("nix-daemon.socket".into()).await?), ], }) }