Create ActionError (#79)

* wip

* Fixes

* Fix a comment
This commit is contained in:
Ana Hobden 2022-12-05 08:55:30 -08:00 committed by GitHub
parent 16e886f53b
commit 4ce8d94ac7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 412 additions and 694 deletions

View file

@ -5,11 +5,8 @@ use nix::unistd::{chown, Group, User};
use tokio::fs::{create_dir, remove_dir_all};
use crate::action::StatefulAction;
use crate::{
action::{Action, ActionDescription, ActionState},
BoxableError,
};
use crate::action::{Action, ActionDescription, ActionState};
use crate::action::{ActionError, StatefulAction};
/** Create a directory at the given location, optionally with an owning user, group, and mode.
@ -33,16 +30,16 @@ impl CreateDirectory {
group: impl Into<Option<String>>,
mode: impl Into<Option<u32>>,
force_prune_on_revert: bool,
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
) -> Result<StatefulAction<Self>, ActionError> {
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).boxed()
})?;
let metadata = tokio::fs::metadata(path)
.await
.map_err(|e| ActionError::GettingMetadata(path.to_path_buf(), e))?;
if metadata.is_dir() {
tracing::debug!(
"Creating directory `{}` already complete, skipping",
@ -51,14 +48,7 @@ impl CreateDirectory {
// TODO: Validate owner/group...
ActionState::Skipped
} else {
return Err(CreateDirectoryError::Exists(std::io::Error::new(
std::io::ErrorKind::AlreadyExists,
format!(
"Path `{}` already exists and is not directory",
path.display()
),
))
.boxed());
return Err(ActionError::Exists(path.to_owned()));
}
} else {
ActionState::Uncompleted
@ -94,7 +84,7 @@ impl Action for CreateDirectory {
group = self.group,
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
))]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self {
path,
user,
@ -106,8 +96,8 @@ impl Action for CreateDirectory {
let gid = if let Some(group) = group {
Some(
Group::from_name(group.as_str())
.map_err(|e| CreateDirectoryError::GroupId(group.clone(), e).boxed())?
.ok_or(CreateDirectoryError::NoGroup(group.clone()).boxed())?
.map_err(|e| ActionError::GroupId(group.clone(), e))?
.ok_or(ActionError::NoGroup(group.clone()))?
.gid,
)
} else {
@ -116,8 +106,8 @@ impl Action for CreateDirectory {
let uid = if let Some(user) = user {
Some(
User::from_name(user.as_str())
.map_err(|e| CreateDirectoryError::UserId(user.clone(), e).boxed())?
.ok_or(CreateDirectoryError::NoUser(user.clone()).boxed())?
.map_err(|e| ActionError::UserId(user.clone(), e))?
.ok_or(ActionError::NoUser(user.clone()))?
.uid,
)
} else {
@ -126,15 +116,13 @@ impl Action for CreateDirectory {
create_dir(path.clone())
.await
.map_err(|e| CreateDirectoryError::Creating(path.clone(), e).boxed())?;
chown(path, uid, gid).map_err(|e| CreateDirectoryError::Chown(path.clone(), e).boxed())?;
.map_err(|e| ActionError::CreateDirectory(path.clone(), e))?;
chown(path, uid, gid).map_err(|e| ActionError::Chown(path.clone(), e))?;
if let Some(mode) = mode {
tokio::fs::set_permissions(&path, PermissionsExt::from_mode(*mode))
.await
.map_err(|e| {
CreateDirectoryError::SetPermissions(*mode, path.to_owned(), e).boxed()
})?;
.map_err(|e| ActionError::SetPermissions(*mode, path.to_owned(), e))?;
}
Ok(())
@ -168,7 +156,7 @@ impl Action for CreateDirectory {
group = self.group,
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
))]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
let Self {
path,
user: _,
@ -179,13 +167,13 @@ impl Action for CreateDirectory {
let is_empty = path
.read_dir()
.map_err(|e| CreateDirectoryError::ReadDir(path.clone(), e).boxed())?
.map_err(|e| ActionError::Read(path.clone(), e))?
.next()
.is_some();
match (is_empty, force_prune_on_revert) {
(true, _) | (false, true) => remove_dir_all(path.clone())
.await
.map_err(|e| CreateDirectoryError::Removing(path.clone(), e).boxed())?,
.map_err(|e| ActionError::Remove(path.clone(), e))?,
(false, false) => {
tracing::debug!("Not removing `{}`, the folder is not empty", path.display());
},
@ -194,29 +182,3 @@ impl Action for CreateDirectory {
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum CreateDirectoryError {
#[error(transparent)]
Exists(std::io::Error),
#[error("Creating directory `{0}`")]
Creating(std::path::PathBuf, #[source] std::io::Error),
#[error("Removing directory `{0}`")]
Removing(std::path::PathBuf, #[source] std::io::Error),
#[error("Getting metadata for {0}`")]
GettingMetadata(std::path::PathBuf, #[source] std::io::Error),
#[error("Reading directory `{0}``")]
ReadDir(std::path::PathBuf, #[source] std::io::Error),
#[error("Set mode `{0}` on `{1}`")]
SetPermissions(u32, std::path::PathBuf, #[source] std::io::Error),
#[error("Chowning directory `{0}`")]
Chown(std::path::PathBuf, #[source] nix::errno::Errno),
#[error("Getting uid for user `{0}`")]
UserId(String, #[source] nix::errno::Errno),
#[error("Getting user `{0}`")]
NoUser(String),
#[error("Getting gid for group `{0}`")]
GroupId(String, #[source] nix::errno::Errno),
#[error("Getting group `{0}`")]
NoGroup(String),
}

View file

@ -6,10 +6,7 @@ use tokio::{
io::AsyncWriteExt,
};
use crate::{
action::{Action, ActionDescription, StatefulAction},
BoxableError,
};
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
/** Create a file at the given location with the provided `buf`,
optionally with an owning user, group, and mode.
@ -36,11 +33,11 @@ impl CreateFile {
mode: impl Into<Option<u32>>,
buf: String,
force: bool,
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
) -> Result<StatefulAction<Self>, ActionError> {
let path = path.as_ref().to_path_buf();
if path.exists() && !force {
return Err(CreateFileError::Exists(path.to_path_buf()).boxed());
return Err(ActionError::Exists(path.to_path_buf()));
}
Ok(Self {
@ -71,7 +68,7 @@ impl Action for CreateFile {
group = self.group,
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
))]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self {
path,
user,
@ -91,17 +88,17 @@ impl Action for CreateFile {
let mut file = options
.open(&path)
.await
.map_err(|e| CreateFileError::OpenFile(path.to_owned(), e).boxed())?;
.map_err(|e| ActionError::Open(path.to_owned(), e))?;
file.write_all(buf.as_bytes())
.await
.map_err(|e| CreateFileError::WriteFile(path.to_owned(), e).boxed())?;
.map_err(|e| ActionError::Write(path.to_owned(), e))?;
let gid = if let Some(group) = group {
Some(
Group::from_name(group.as_str())
.map_err(|e| CreateFileError::GroupId(group.clone(), e).boxed())?
.ok_or(CreateFileError::NoGroup(group.clone()).boxed())?
.map_err(|e| ActionError::GroupId(group.clone(), e))?
.ok_or(ActionError::NoGroup(group.clone()))?
.gid,
)
} else {
@ -110,14 +107,14 @@ impl Action for CreateFile {
let uid = if let Some(user) = user {
Some(
User::from_name(user.as_str())
.map_err(|e| CreateFileError::UserId(user.clone(), e).boxed())?
.ok_or(CreateFileError::NoUser(user.clone()).boxed())?
.map_err(|e| ActionError::UserId(user.clone(), e))?
.ok_or(ActionError::NoUser(user.clone()))?
.uid,
)
} else {
None
};
chown(path, uid, gid).map_err(|e| CreateFileError::Chown(path.clone(), e).boxed())?;
chown(path, uid, gid).map_err(|e| ActionError::Chown(path.clone(), e))?;
Ok(())
}
@ -144,7 +141,7 @@ impl Action for CreateFile {
group = self.group,
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
))]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
let Self {
path,
user: _,
@ -156,30 +153,8 @@ impl Action for CreateFile {
remove_file(&path)
.await
.map_err(|e| CreateFileError::RemoveFile(path.to_owned(), e).boxed())?;
.map_err(|e| ActionError::Remove(path.to_owned(), e))?;
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum CreateFileError {
#[error("File exists `{0}`")]
Exists(std::path::PathBuf),
#[error("Remove file `{0}`")]
RemoveFile(std::path::PathBuf, #[source] std::io::Error),
#[error("Open file `{0}`")]
OpenFile(std::path::PathBuf, #[source] std::io::Error),
#[error("Write file `{0}`")]
WriteFile(std::path::PathBuf, #[source] std::io::Error),
#[error("Getting uid for user `{0}`")]
UserId(String, #[source] nix::errno::Errno),
#[error("Getting user `{0}`")]
NoUser(String),
#[error("Getting gid for group `{0}`")]
GroupId(String, #[source] nix::errno::Errno),
#[error("Getting group `{0}`")]
NoGroup(String),
#[error("Chowning directory `{0}`")]
Chown(std::path::PathBuf, #[source] nix::errno::Errno),
}

View file

@ -1,11 +1,9 @@
use tokio::process::Command;
use crate::action::ActionError;
use crate::execute_command;
use crate::{
action::{Action, ActionDescription, StatefulAction},
BoxableError,
};
use crate::action::{Action, ActionDescription, StatefulAction};
/**
Create an operating system level user group
@ -43,7 +41,7 @@ impl Action for CreateGroup {
user = self.name,
gid = self.gid,
))]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self { name, gid } = self;
use target_lexicon::OperatingSystem;
@ -60,7 +58,8 @@ impl Action for CreateGroup {
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.status()
.await?
.await
.map_err(ActionError::Command)?
.success()
{
()
@ -80,7 +79,7 @@ impl Action for CreateGroup {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| CreateGroupError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
}
},
_ => {
@ -91,7 +90,7 @@ impl Action for CreateGroup {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| CreateGroupError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
},
};
@ -112,7 +111,7 @@ impl Action for CreateGroup {
user = self.name,
gid = self.gid,
))]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
let Self { name, gid: _ } = self;
use target_lexicon::OperatingSystem;
@ -142,16 +141,10 @@ impl Action for CreateGroup {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| CreateGroupError::Command(e).boxed())?;
.map_err(ActionError::Command)?;
},
};
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum CreateGroupError {
#[error("Failed to execute command")]
Command(#[source] std::io::Error),
}

View file

@ -10,10 +10,7 @@ use tokio::{
io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt},
};
use crate::{
action::{Action, ActionDescription, StatefulAction},
BoxableError,
};
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
/** Create a file at the given location with the provided `buf`,
optionally with an owning user, group, and mode.
@ -40,7 +37,7 @@ impl CreateOrAppendFile {
group: impl Into<Option<String>>,
mode: impl Into<Option<u32>>,
buf: String,
) -> Result<StatefulAction<Self>, CreateOrAppendFileError> {
) -> Result<StatefulAction<Self>, ActionError> {
let path = path.as_ref().to_path_buf();
Ok(Self {
@ -71,7 +68,7 @@ impl Action for CreateOrAppendFile {
group = self.group,
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
))]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self {
path,
user,
@ -86,21 +83,21 @@ impl Action for CreateOrAppendFile {
.read(true)
.open(&path)
.await
.map_err(|e| CreateOrAppendFileError::OpenFile(path.to_owned(), e).boxed())?;
.map_err(|e| ActionError::Open(path.to_owned(), e))?;
file.seek(SeekFrom::End(0))
.await
.map_err(|e| CreateOrAppendFileError::SeekFile(path.to_owned(), e).boxed())?;
.map_err(|e| ActionError::Seek(path.to_owned(), e))?;
file.write_all(buf.as_bytes())
.await
.map_err(|e| CreateOrAppendFileError::WriteFile(path.to_owned(), e).boxed())?;
.map_err(|e| ActionError::Write(path.to_owned(), e))?;
let gid = if let Some(group) = group {
Some(
Group::from_name(group.as_str())
.map_err(|e| CreateOrAppendFileError::GroupId(group.clone(), e).boxed())?
.ok_or(CreateOrAppendFileError::NoGroup(group.clone()).boxed())?
.map_err(|e| ActionError::GroupId(group.clone(), e))?
.ok_or(ActionError::NoGroup(group.clone()))?
.gid,
)
} else {
@ -109,8 +106,8 @@ impl Action for CreateOrAppendFile {
let uid = if let Some(user) = user {
Some(
User::from_name(user.as_str())
.map_err(|e| CreateOrAppendFileError::UserId(user.clone(), e).boxed())?
.ok_or(CreateOrAppendFileError::NoUser(user.clone()).boxed())?
.map_err(|e| ActionError::UserId(user.clone(), e))?
.ok_or(ActionError::NoUser(user.clone()))?
.uid,
)
} else {
@ -120,13 +117,10 @@ impl Action for CreateOrAppendFile {
if let Some(mode) = mode {
tokio::fs::set_permissions(&path, PermissionsExt::from_mode(*mode))
.await
.map_err(|e| {
CreateOrAppendFileError::SetPermissions(*mode, path.to_owned(), e).boxed()
})?;
.map_err(|e| ActionError::SetPermissions(*mode, path.to_owned(), e))?;
}
chown(path, uid, gid)
.map_err(|e| CreateOrAppendFileError::Chown(path.clone(), e).boxed())?;
chown(path, uid, gid).map_err(|e| ActionError::Chown(path.clone(), e))?;
Ok(())
}
@ -154,7 +148,7 @@ impl Action for CreateOrAppendFile {
group = self.group,
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
))]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
let Self {
path,
user: _,
@ -168,12 +162,12 @@ impl Action for CreateOrAppendFile {
.read(true)
.open(&path)
.await
.map_err(|e| CreateOrAppendFileError::ReadFile(path.to_owned(), e).boxed())?;
.map_err(|e| ActionError::Read(path.to_owned(), e))?;
let mut file_contents = String::default();
file.read_to_string(&mut file_contents)
.await
.map_err(|e| CreateOrAppendFileError::SeekFile(path.to_owned(), e).boxed())?;
.map_err(|e| ActionError::Seek(path.to_owned(), e))?;
if let Some(start) = file_contents.rfind(buf.as_str()) {
let end = start + buf.len();
@ -183,41 +177,15 @@ impl Action for CreateOrAppendFile {
if buf.is_empty() {
remove_file(&path)
.await
.map_err(|e| CreateOrAppendFileError::RemoveFile(path.to_owned(), e).boxed())?;
.map_err(|e| ActionError::Remove(path.to_owned(), e))?;
} else {
file.seek(SeekFrom::Start(0))
.await
.map_err(|e| CreateOrAppendFileError::SeekFile(path.to_owned(), e).boxed())?;
.map_err(|e| ActionError::Seek(path.to_owned(), e))?;
file.write_all(file_contents.as_bytes())
.await
.map_err(|e| CreateOrAppendFileError::WriteFile(path.to_owned(), e).boxed())?;
.map_err(|e| ActionError::Write(path.to_owned(), e))?;
}
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum CreateOrAppendFileError {
#[error("Remove file `{0}`")]
RemoveFile(std::path::PathBuf, #[source] std::io::Error),
#[error("Remove file `{0}`")]
ReadFile(std::path::PathBuf, #[source] std::io::Error),
#[error("Open file `{0}`")]
OpenFile(std::path::PathBuf, #[source] std::io::Error),
#[error("Write file `{0}`")]
WriteFile(std::path::PathBuf, #[source] std::io::Error),
#[error("Seek file `{0}`")]
SeekFile(std::path::PathBuf, #[source] std::io::Error),
#[error("Getting uid for user `{0}`")]
UserId(String, #[source] nix::errno::Errno),
#[error("Getting user `{0}`")]
NoUser(String),
#[error("Getting gid for group `{0}`")]
GroupId(String, #[source] nix::errno::Errno),
#[error("Getting group `{0}`")]
NoGroup(String),
#[error("Set mode `{0}` on `{1}`")]
SetPermissions(u32, std::path::PathBuf, #[source] std::io::Error),
#[error("Chowning directory `{0}`")]
Chown(std::path::PathBuf, #[source] nix::errno::Errno),
}

View file

@ -1,11 +1,9 @@
use tokio::process::Command;
use crate::action::ActionError;
use crate::execute_command;
use crate::{
action::{Action, ActionDescription, StatefulAction},
BoxableError,
};
use crate::action::{Action, ActionDescription, StatefulAction};
/**
Create an operating system level user in the given group
@ -55,7 +53,7 @@ impl Action for CreateUser {
groupname = self.groupname,
gid = self.gid,
))]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self {
name,
uid,
@ -80,7 +78,8 @@ impl Action for CreateUser {
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.status()
.await?
.await
.map_err(ActionError::Command)?
.success()
{
()
@ -92,7 +91,7 @@ impl Action for CreateUser {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| CreateUserError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
execute_command(
Command::new("/usr/bin/dscl")
.process_group(0)
@ -106,7 +105,7 @@ impl Action for CreateUser {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| CreateUserError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
execute_command(
Command::new("/usr/bin/dscl")
.process_group(0)
@ -120,7 +119,7 @@ impl Action for CreateUser {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| CreateUserError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
execute_command(
Command::new("/usr/bin/dscl")
.process_group(0)
@ -134,7 +133,7 @@ impl Action for CreateUser {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| CreateUserError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
execute_command(
Command::new("/usr/bin/dscl")
.process_group(0)
@ -148,7 +147,7 @@ impl Action for CreateUser {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| CreateUserError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
execute_command(
Command::new("/usr/bin/dscl")
.process_group(0)
@ -162,7 +161,7 @@ impl Action for CreateUser {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| CreateUserError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
execute_command(
Command::new("/usr/bin/dscl")
.process_group(0)
@ -170,7 +169,7 @@ impl Action for CreateUser {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| CreateUserError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
execute_command(
Command::new("/usr/sbin/dseditgroup")
.process_group(0)
@ -183,7 +182,7 @@ impl Action for CreateUser {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| CreateUserError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
}
},
_ => {
@ -212,7 +211,7 @@ impl Action for CreateUser {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| CreateUserError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
},
}
@ -236,7 +235,7 @@ impl Action for CreateUser {
uid = self.uid,
gid = self.gid,
))]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
let Self {
name,
uid: _,
@ -271,16 +270,10 @@ impl Action for CreateUser {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| CreateUserError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
},
};
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum CreateUserError {
#[error("Failed to execute command")]
Command(#[source] std::io::Error),
}

View file

@ -3,12 +3,7 @@ use std::path::PathBuf;
use bytes::Buf;
use reqwest::Url;
use tokio::task::JoinError;
use crate::{
action::{Action, ActionDescription, StatefulAction},
BoxableError,
};
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
/**
Fetch a URL to the given path
@ -21,7 +16,7 @@ pub struct FetchAndUnpackNix {
impl FetchAndUnpackNix {
#[tracing::instrument(skip_all)]
pub async fn plan(url: Url, dest: PathBuf) -> Result<StatefulAction<Self>, FetchUrlError> {
pub async fn plan(url: Url, dest: PathBuf) -> Result<StatefulAction<Self>, ActionError> {
// TODO(@hoverbear): Check URL exists?
// TODO(@hoverbear): Check tempdir exists
@ -44,16 +39,16 @@ impl Action for FetchAndUnpackNix {
url = %self.url,
dest = %self.dest.display(),
))]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self { url, dest } = self;
let res = reqwest::get(url.clone())
.await
.map_err(|e| FetchUrlError::Reqwest(e).boxed())?;
.map_err(|e| ActionError::Custom(Box::new(FetchUrlError::Reqwest(e))))?;
let bytes = res
.bytes()
.await
.map_err(|e| FetchUrlError::Reqwest(e).boxed())?;
.map_err(|e| ActionError::Custom(Box::new(FetchUrlError::Reqwest(e))))?;
// TODO(@Hoverbear): Pick directory
tracing::trace!("Unpacking tar.xz");
let dest_clone = dest.clone();
@ -62,7 +57,7 @@ impl Action for FetchAndUnpackNix {
let mut archive = tar::Archive::new(decoder);
archive
.unpack(&dest_clone)
.map_err(|e| FetchUrlError::Unarchive(e).boxed())?;
.map_err(|e| ActionError::Custom(Box::new(FetchUrlError::Unarchive(e))))?;
Ok(())
}
@ -75,7 +70,7 @@ impl Action for FetchAndUnpackNix {
url = %self.url,
dest = %self.dest.display(),
))]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
let Self { url: _, dest: _ } = self;
Ok(())
@ -84,12 +79,6 @@ impl Action for FetchAndUnpackNix {
#[derive(Debug, thiserror::Error)]
pub enum FetchUrlError {
#[error("Joining spawned async task")]
Join(
#[source]
#[from]
JoinError,
),
#[error("Request error")]
Reqwest(
#[from]

View file

@ -9,11 +9,11 @@ mod fetch_and_unpack_nix;
mod move_unpacked_nix;
mod setup_default_profile;
pub use create_directory::{CreateDirectory, CreateDirectoryError};
pub use create_file::{CreateFile, CreateFileError};
pub use create_group::{CreateGroup, CreateGroupError};
pub use create_or_append_file::{CreateOrAppendFile, CreateOrAppendFileError};
pub use create_user::{CreateUser, CreateUserError};
pub use create_directory::CreateDirectory;
pub use create_file::CreateFile;
pub use create_group::CreateGroup;
pub use create_or_append_file::CreateOrAppendFile;
pub use create_user::CreateUser;
pub use fetch_and_unpack_nix::{FetchAndUnpackNix, FetchUrlError};
pub use move_unpacked_nix::{MoveUnpackedNix, MoveUnpackedNixError};
pub use setup_default_profile::{SetupDefaultProfile, SetupDefaultProfileError};

View file

@ -1,9 +1,6 @@
use std::path::{Path, PathBuf};
use crate::{
action::{Action, ActionDescription, StatefulAction},
BoxableError,
};
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
const DEST: &str = "/nix/store";
@ -17,7 +14,7 @@ pub struct MoveUnpackedNix {
impl MoveUnpackedNix {
#[tracing::instrument(skip_all)]
pub async fn plan(src: PathBuf) -> Result<StatefulAction<Self>, MoveUnpackedNixError> {
pub async fn plan(src: PathBuf) -> Result<StatefulAction<Self>, ActionError> {
// Note: Do NOT try to check for the src/dest since the installer creates those
Ok(Self { src }.into())
}
@ -44,14 +41,14 @@ impl Action for MoveUnpackedNix {
src = %self.src.display(),
dest = DEST,
))]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self { src } = self;
// TODO(@Hoverbear): I would like to make this less awful
let found_nix_paths = glob::glob(&format!("{}/nix-*", src.display()))
.map_err(|e| e.boxed())?
.map_err(|e| ActionError::Custom(Box::new(e)))?
.collect::<Result<Vec<_>, _>>()
.map_err(|e| e.boxed())?;
.map_err(|e| ActionError::Custom(Box::new(e)))?;
assert_eq!(
found_nix_paths.len(),
1,
@ -63,13 +60,11 @@ impl Action for MoveUnpackedNix {
tracing::trace!(src = %src_store.display(), dest = %dest.display(), "Renaming");
tokio::fs::rename(src_store.clone(), dest)
.await
.map_err(|e| {
MoveUnpackedNixError::Rename(src_store.clone(), dest.to_owned(), e).boxed()
})?;
.map_err(|e| ActionError::Rename(src_store.clone(), dest.to_owned(), e))?;
tokio::fs::remove_dir_all(src)
.await
.map_err(|e| MoveUnpackedNixError::Rename(src_store, dest.to_owned(), e).boxed())?;
.map_err(|e| ActionError::Rename(src_store, dest.to_owned(), e))?;
Ok(())
}
@ -82,7 +77,7 @@ impl Action for MoveUnpackedNix {
src = %self.src.display(),
dest = DEST,
))]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
// Noop
Ok(())
}
@ -102,10 +97,4 @@ pub enum MoveUnpackedNixError {
#[source]
glob::GlobError,
),
#[error("Rename `{0}` to `{1}`")]
Rename(
std::path::PathBuf,
std::path::PathBuf,
#[source] std::io::Error,
),
}

View file

@ -1,4 +1,7 @@
use crate::{action::StatefulAction, execute_command, set_env, BoxableError};
use crate::{
action::{ActionError, StatefulAction},
execute_command, set_env,
};
use glob::glob;
@ -16,9 +19,7 @@ pub struct SetupDefaultProfile {
impl SetupDefaultProfile {
#[tracing::instrument(skip_all)]
pub async fn plan(
channels: Vec<String>,
) -> Result<StatefulAction<Self>, SetupDefaultProfileError> {
pub async fn plan(channels: Vec<String>) -> Result<StatefulAction<Self>, ActionError> {
Ok(Self { channels }.into())
}
}
@ -37,15 +38,15 @@ impl Action for SetupDefaultProfile {
#[tracing::instrument(skip_all, fields(
channels = %self.channels.join(","),
))]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self { channels } = self;
// 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(|e| SetupDefaultProfileError::GlobPatternError(e).boxed())?
{
for entry in glob(nix_pkg_glob).map_err(|e| {
ActionError::Custom(Box::new(SetupDefaultProfileError::GlobPatternError(e)))
})? {
match entry {
Ok(path) => {
// TODO(@Hoverbear): Should probably ensure is unique
@ -58,15 +59,17 @@ impl Action for SetupDefaultProfile {
let nix_pkg = if let Some(nix_pkg) = found_nix_pkg {
nix_pkg
} else {
return Err(Box::new(SetupDefaultProfileError::NoNssCacert)); // TODO(@hoverbear): Fix this error
return Err(ActionError::Custom(Box::new(
SetupDefaultProfileError::NoNix,
)));
};
// 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(|e| SetupDefaultProfileError::GlobPatternError(e).boxed())?
{
for entry in glob(nss_ca_cert_pkg_glob).map_err(|e| {
ActionError::Custom(Box::new(SetupDefaultProfileError::GlobPatternError(e)))
})? {
match entry {
Ok(path) => {
// TODO(@Hoverbear): Should probably ensure is unique
@ -79,7 +82,9 @@ impl Action 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(Box::new(SetupDefaultProfileError::NoNssCacert));
return Err(ActionError::Custom(Box::new(
SetupDefaultProfileError::NoNssCacert,
)));
};
// Install `nix` itself into the store
@ -93,7 +98,9 @@ impl Action for SetupDefaultProfile {
.stdin(std::process::Stdio::null())
.env(
"HOME",
dirs::home_dir().ok_or_else(|| SetupDefaultProfileError::NoRootHome.boxed())?,
dirs::home_dir().ok_or_else(|| {
ActionError::Custom(Box::new(SetupDefaultProfileError::NoRootHome))
})?,
)
.env(
"NIX_SSL_CERT_FILE",
@ -101,7 +108,7 @@ impl Action for SetupDefaultProfile {
), /* This is apparently load bearing... */
)
.await
.map_err(|e| SetupDefaultProfileError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
// Install `nss-cacert` into the store
// execute_command(
@ -136,7 +143,7 @@ impl Action for SetupDefaultProfile {
execute_command(&mut command)
.await
.map_err(|e| SetupDefaultProfileError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
}
Ok(())
@ -152,7 +159,7 @@ impl Action for SetupDefaultProfile {
#[tracing::instrument(skip_all, fields(
channels = %self.channels.join(","),
))]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
std::env::remove_var("NIX_SSL_CERT_FILE");
Ok(())
@ -175,8 +182,8 @@ pub enum SetupDefaultProfileError {
),
#[error("Unarchived Nix store did not appear to include a `nss-cacert` location")]
NoNssCacert,
#[error("Failed to execute command")]
Command(#[source] std::io::Error),
#[error("Unarchived Nix store did not appear to include a `nix` location")]
NoNix,
#[error("No root home found to place channel configuration in")]
NoRootHome,
}

View file

@ -3,11 +3,10 @@ use crate::{
base::SetupDefaultProfile,
common::{ConfigureShellProfile, PlaceChannelConfiguration, PlaceNixConfiguration},
linux::ConfigureNixDaemonService,
Action, ActionDescription, StatefulAction,
Action, ActionDescription, ActionError, StatefulAction,
},
channel_value::ChannelValue,
settings::CommonSettings,
BoxableError,
};
use reqwest::Url;
@ -26,9 +25,7 @@ pub struct ConfigureNix {
impl ConfigureNix {
#[tracing::instrument(skip_all)]
pub async fn plan(
settings: &CommonSettings,
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
pub async fn plan(settings: &CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
let channels: Vec<(String, Url)> = settings
.channels
.iter()
@ -36,9 +33,7 @@ impl ConfigureNix {
.collect();
let setup_default_profile =
SetupDefaultProfile::plan(channels.iter().map(|(v, _k)| v.clone()).collect())
.await
.map_err(|e| e.boxed())?;
SetupDefaultProfile::plan(channels.iter().map(|(v, _k)| v.clone()).collect()).await?;
let configure_shell_profile = if settings.modify_profile {
Some(ConfigureShellProfile::plan().await?)
@ -93,7 +88,7 @@ impl Action for ConfigureNix {
}
#[tracing::instrument(skip_all)]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self {
setup_default_profile,
configure_nix_daemon_service,
@ -143,7 +138,7 @@ impl Action for ConfigureNix {
}
#[tracing::instrument(skip_all)]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
let Self {
setup_default_profile,
configure_nix_daemon_service,

View file

@ -1,9 +1,8 @@
use crate::action::base::{CreateOrAppendFile, CreateOrAppendFileError};
use crate::action::{Action, ActionDescription, StatefulAction};
use crate::BoxableError;
use crate::action::base::CreateOrAppendFile;
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
use std::path::Path;
use tokio::task::{JoinError, JoinSet};
use tokio::task::JoinSet;
const PROFILE_TARGETS: &[&str] = &[
"/etc/bashrc",
@ -25,7 +24,7 @@ pub struct ConfigureShellProfile {
impl ConfigureShellProfile {
#[tracing::instrument(skip_all)]
pub async fn plan() -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
pub async fn plan() -> Result<StatefulAction<Self>, ActionError> {
let mut create_or_append_files = Vec::default();
for profile_target in PROFILE_TARGETS {
let path = Path::new(profile_target);
@ -42,11 +41,8 @@ impl ConfigureShellProfile {
# End Nix\n
\n",
);
create_or_append_files.push(
CreateOrAppendFile::plan(path, None, None, 0o0644, buf)
.await
.map_err(|e| e.boxed())?,
);
create_or_append_files
.push(CreateOrAppendFile::plan(path, None, None, 0o0644, buf).await?);
}
Ok(Self {
@ -71,7 +67,7 @@ impl Action for ConfigureShellProfile {
}
#[tracing::instrument(skip_all)]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self {
create_or_append_files,
} = self;
@ -83,10 +79,7 @@ impl Action 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.try_execute().await?;
Result::<_, Box<dyn std::error::Error + Send + Sync>>::Ok((
idx,
create_or_append_file_clone,
))
Result::<_, ActionError>::Ok((idx, create_or_append_file_clone))
});
}
@ -95,8 +88,8 @@ impl Action for ConfigureShellProfile {
Ok(Ok((idx, create_or_append_file))) => {
create_or_append_files[idx] = create_or_append_file
},
Ok(Err(e)) => errors.push(e),
Err(e) => return Err(e.boxed()),
Ok(Err(e)) => errors.push(Box::new(e)),
Err(e) => return Err(e.into()),
};
}
@ -104,7 +97,7 @@ impl Action for ConfigureShellProfile {
if errors.len() == 1 {
return Err(errors.into_iter().next().unwrap().into());
} else {
return Err(ConfigureShellProfileError::MultipleCreateOrAppendFile(errors).boxed());
return Err(ActionError::Children(errors));
}
}
@ -119,22 +112,19 @@ impl Action for ConfigureShellProfile {
}
#[tracing::instrument(skip_all)]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
let Self {
create_or_append_files,
} = self;
let mut set = JoinSet::new();
let mut errors = Vec::default();
let mut errors: Vec<Box<ActionError>> = Vec::default();
for (idx, create_or_append_file) in create_or_append_files.iter().enumerate() {
let mut create_or_append_file_clone = create_or_append_file.clone();
let _abort_handle = set.spawn(async move {
create_or_append_file_clone.try_revert().await?;
Result::<_, Box<dyn std::error::Error + Send + Sync>>::Ok((
idx,
create_or_append_file_clone,
))
Result::<_, _>::Ok((idx, create_or_append_file_clone))
});
}
@ -143,8 +133,8 @@ impl Action for ConfigureShellProfile {
Ok(Ok((idx, create_or_append_file))) => {
create_or_append_files[idx] = create_or_append_file
},
Ok(Err(e)) => errors.push(e),
Err(e) => return Err(e.boxed()),
Ok(Err(e)) => errors.push(Box::new(e)),
Err(e) => return Err(e.into()),
};
}
@ -152,34 +142,10 @@ impl Action for ConfigureShellProfile {
if errors.len() == 1 {
return Err(errors.into_iter().next().unwrap().into());
} else {
return Err(ConfigureShellProfileError::MultipleCreateOrAppendFile(errors).boxed());
return Err(ActionError::Children(errors));
}
}
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum ConfigureShellProfileError {
#[error("Creating or appending to file")]
CreateOrAppendFile(
#[from]
#[source]
CreateOrAppendFileError,
),
#[error("Multiple errors: {}", .0.iter().map(|v| {
if let Some(source) = v.source() {
format!("{v} ({source})")
} else {
format!("{v}")
}
}).collect::<Vec<_>>().join(" & "))]
MultipleCreateOrAppendFile(Vec<Box<dyn std::error::Error + Send + Sync>>),
#[error("Joining spawned async task")]
Join(
#[source]
#[from]
JoinError,
),
}

View file

@ -1,5 +1,5 @@
use crate::action::base::{CreateDirectory, CreateDirectoryError};
use crate::action::{Action, ActionDescription, StatefulAction};
use crate::action::base::CreateDirectory;
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
const PATHS: &[&str] = &[
"/nix/var",
@ -27,7 +27,7 @@ pub struct CreateNixTree {
impl CreateNixTree {
#[tracing::instrument(skip_all)]
pub async fn plan() -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
pub async fn plan() -> Result<StatefulAction<Self>, ActionError> {
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
@ -65,7 +65,7 @@ impl Action for CreateNixTree {
}
#[tracing::instrument(skip_all)]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self { create_directories } = self;
// Just do sequential since parallelizing this will have little benefit
@ -97,7 +97,7 @@ impl Action for CreateNixTree {
}
#[tracing::instrument(skip_all)]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
let Self { create_directories } = self;
// Just do sequential since parallelizing this will have little benefit
@ -108,13 +108,3 @@ impl Action for CreateNixTree {
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum CreateNixTreeError {
#[error("Creating directory")]
CreateDirectory(
#[source]
#[from]
CreateDirectoryError,
),
}

View file

@ -1,12 +1,11 @@
use crate::{
action::{
base::{CreateGroup, CreateGroupError, CreateUser, CreateUserError},
Action, ActionDescription, StatefulAction,
base::{CreateGroup, CreateUser},
Action, ActionDescription, ActionError, StatefulAction,
},
settings::CommonSettings,
BoxableError,
};
use tokio::task::{JoinError, JoinSet};
use tokio::task::JoinSet;
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CreateUsersAndGroups {
@ -21,9 +20,7 @@ pub struct CreateUsersAndGroups {
impl CreateUsersAndGroups {
#[tracing::instrument(skip_all)]
pub async fn plan(
settings: CommonSettings,
) -> Result<StatefulAction<Self>, CreateUsersAndGroupsError> {
pub async fn plan(settings: CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
// TODO(@hoverbear): CHeck if it exist, error if so
let create_group = CreateGroup::plan(
settings.nix_build_group_name.clone(),
@ -101,7 +98,7 @@ impl Action for CreateUsersAndGroups {
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<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self {
create_users,
create_group,
@ -130,7 +127,7 @@ impl Action for CreateUsersAndGroups {
},
_ => {
let mut set = JoinSet::new();
let mut errors: Vec<Box<dyn std::error::Error + Send + Sync>> = Vec::new();
let mut errors: Vec<Box<ActionError>> = Vec::new();
for (idx, create_user) in create_users.iter_mut().enumerate() {
let mut create_user_clone = create_user.clone();
let _abort_handle = set.spawn(async move {
@ -142,8 +139,8 @@ impl Action for CreateUsersAndGroups {
while let Some(result) = set.join_next().await {
match result {
Ok(Ok((idx, success))) => create_users[idx] = success,
Ok(Err(e)) => errors.push(e),
Err(e) => return Err(e)?,
Ok(Err(e)) => errors.push(Box::new(e)),
Err(e) => return Err(ActionError::Join(e))?,
};
}
@ -151,7 +148,7 @@ impl Action for CreateUsersAndGroups {
if errors.len() == 1 {
return Err(errors.into_iter().next().unwrap().into());
} else {
return Err(CreateUsersAndGroupsError::CreateUsers(errors).boxed());
return Err(ActionError::Children(errors));
}
}
},
@ -198,7 +195,7 @@ impl Action for CreateUsersAndGroups {
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<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
let Self {
create_users,
create_group,
@ -216,15 +213,15 @@ impl Action for CreateUsersAndGroups {
let mut create_user_clone = create_user.clone();
let _abort_handle = set.spawn(async move {
create_user_clone.try_revert().await?;
Result::<_, Box<dyn std::error::Error + Send + Sync>>::Ok((idx, create_user_clone))
Result::<_, ActionError>::Ok((idx, create_user_clone))
});
}
while let Some(result) = set.join_next().await {
match result {
Ok(Ok((idx, success))) => create_users[idx] = success,
Ok(Err(e)) => errors.push(e),
Err(e) => return Err(e.boxed())?,
Ok(Err(e)) => errors.push(Box::new(e)),
Err(e) => return Err(ActionError::Join(e))?,
};
}
@ -232,7 +229,7 @@ impl Action for CreateUsersAndGroups {
if errors.len() == 1 {
return Err(errors.into_iter().next().unwrap().into());
} else {
return Err(CreateUsersAndGroupsError::CreateUsers(errors).boxed());
return Err(ActionError::Children(errors));
}
}
@ -242,33 +239,3 @@ impl Action for CreateUsersAndGroups {
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum CreateUsersAndGroupsError {
#[error("Creating user")]
CreateUser(
#[source]
#[from]
CreateUserError,
),
#[error("Multiple errors: {}", .0.iter().map(|v| {
if let Some(source) = v.source() {
format!("{v} ({source})")
} else {
format!("{v}")
}
}).collect::<Vec<_>>().join(" & "))]
CreateUsers(Vec<Box<dyn std::error::Error + Send + Sync>>),
#[error("Creating group")]
CreateGroup(
#[source]
#[from]
CreateGroupError,
),
#[error("Joining spawned async task")]
Join(
#[source]
#[from]
JoinError,
),
}

View file

@ -10,8 +10,8 @@ mod provision_nix;
pub use configure_nix::ConfigureNix;
pub use configure_shell_profile::ConfigureShellProfile;
pub use create_nix_tree::{CreateNixTree, CreateNixTreeError};
pub use create_users_and_groups::{CreateUsersAndGroups, CreateUsersAndGroupsError};
pub use create_nix_tree::CreateNixTree;
pub use create_users_and_groups::CreateUsersAndGroups;
pub use place_channel_configuration::{PlaceChannelConfiguration, PlaceChannelConfigurationError};
pub use place_nix_configuration::{PlaceNixConfiguration, PlaceNixConfigurationError};
pub use provision_nix::{ProvisionNix, ProvisionNixError};
pub use place_nix_configuration::PlaceNixConfiguration;
pub use provision_nix::ProvisionNix;

View file

@ -1,8 +1,6 @@
use crate::action::base::{CreateFile, CreateFileError};
use crate::{
action::{Action, ActionDescription, StatefulAction},
BoxableError,
};
use crate::action::base::CreateFile;
use crate::action::ActionError;
use crate::action::{Action, ActionDescription, StatefulAction};
use reqwest::Url;
/**
@ -19,7 +17,7 @@ impl PlaceChannelConfiguration {
pub async fn plan(
channels: Vec<(String, Url)>,
force: bool,
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
) -> Result<StatefulAction<Self>, ActionError> {
let buf = channels
.iter()
.map(|(name, url)| format!("{} {}", url, name))
@ -27,7 +25,9 @@ impl PlaceChannelConfiguration {
.join("\n");
let create_file = CreateFile::plan(
dirs::home_dir()
.ok_or_else(|| PlaceChannelConfigurationError::NoRootHome.boxed())?
.ok_or_else(|| {
ActionError::Custom(Box::new(PlaceChannelConfigurationError::NoRootHome))
})?
.join(".nix-channels"),
None,
None,
@ -61,7 +61,7 @@ impl Action for PlaceChannelConfiguration {
#[tracing::instrument(skip_all, fields(
channels = self.channels.iter().map(|(c, u)| format!("{c}={u}")).collect::<Vec<_>>().join(", "),
))]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self {
create_file,
channels: _,
@ -85,7 +85,7 @@ impl Action for PlaceChannelConfiguration {
#[tracing::instrument(skip_all, fields(
channels = self.channels.iter().map(|(c, u)| format!("{c}={u}")).collect::<Vec<_>>().join(", "),
))]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
let Self {
create_file,
channels: _,
@ -99,12 +99,6 @@ impl Action for PlaceChannelConfiguration {
#[derive(Debug, thiserror::Error)]
pub enum PlaceChannelConfigurationError {
#[error("Creating file")]
CreateFile(
#[source]
#[from]
CreateFileError,
),
#[error("No root home found to place channel configuration in")]
NoRootHome,
}

View file

@ -1,5 +1,5 @@
use crate::action::base::{CreateDirectory, CreateDirectoryError, CreateFile, CreateFileError};
use crate::action::{Action, ActionDescription, StatefulAction};
use crate::action::base::{CreateDirectory, CreateFile};
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
const NIX_CONF_FOLDER: &str = "/etc/nix";
const NIX_CONF: &str = "/etc/nix/nix.conf";
@ -19,7 +19,7 @@ impl PlaceNixConfiguration {
nix_build_group_name: String,
extra_conf: Vec<String>,
force: bool,
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
) -> Result<StatefulAction<Self>, ActionError> {
let buf = format!(
"\
{extra_conf}\n\
@ -61,7 +61,7 @@ impl Action for PlaceNixConfiguration {
}
#[tracing::instrument(skip_all)]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self {
create_file,
create_directory,
@ -84,7 +84,7 @@ impl Action for PlaceNixConfiguration {
}
#[tracing::instrument(skip_all)]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
let Self {
create_file,
create_directory,
@ -96,19 +96,3 @@ impl Action for PlaceNixConfiguration {
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum PlaceNixConfigurationError {
#[error("Creating file")]
CreateFile(
#[source]
#[from]
CreateFileError,
),
#[error("Creating directory")]
CreateDirectory(
#[source]
#[from]
CreateDirectoryError,
),
}

View file

@ -1,18 +1,12 @@
use super::{CreateNixTree, CreateUsersAndGroups};
use crate::{
action::{
base::{
CreateDirectoryError, FetchAndUnpackNix, FetchUrlError, MoveUnpackedNix,
MoveUnpackedNixError,
},
Action, ActionDescription, StatefulAction,
base::{FetchAndUnpackNix, MoveUnpackedNix},
Action, ActionDescription, ActionError, StatefulAction,
},
settings::CommonSettings,
BoxableError,
};
use std::path::PathBuf;
use tokio::task::JoinError;
use super::{CreateNixTree, CreateNixTreeError, CreateUsersAndGroups, CreateUsersAndGroupsError};
/**
Place Nix and it's requirements onto the target
@ -27,22 +21,16 @@ pub struct ProvisionNix {
impl ProvisionNix {
#[tracing::instrument(skip_all)]
pub async fn plan(
settings: &CommonSettings,
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
pub async fn plan(settings: &CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
let fetch_nix = FetchAndUnpackNix::plan(
settings.nix_package_url.clone(),
PathBuf::from("/nix/temp-install-dir"),
)
.await
.map_err(|e| e.boxed())?;
let create_users_and_group = CreateUsersAndGroups::plan(settings.clone())
.await
.map_err(|e| e.boxed())?;
.await?;
let create_users_and_group = CreateUsersAndGroups::plan(settings.clone()).await?;
let create_nix_tree = CreateNixTree::plan().await?;
let move_unpacked_nix = MoveUnpackedNix::plan(PathBuf::from("/nix/temp-install-dir"))
.await
.map_err(|e| e.boxed())?;
let move_unpacked_nix =
MoveUnpackedNix::plan(PathBuf::from("/nix/temp-install-dir")).await?;
Ok(Self {
fetch_nix,
create_users_and_group,
@ -78,7 +66,7 @@ impl Action for ProvisionNix {
}
#[tracing::instrument(skip_all)]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self {
fetch_nix,
create_nix_tree,
@ -90,13 +78,13 @@ impl Action for ProvisionNix {
let mut fetch_nix_clone = fetch_nix.clone();
let fetch_nix_handle = tokio::task::spawn(async {
fetch_nix_clone.try_execute().await?;
Result::<_, Box<dyn std::error::Error + Send + Sync>>::Ok(fetch_nix_clone)
Result::<_, ActionError>::Ok(fetch_nix_clone)
});
create_users_and_group.try_execute().await?;
create_nix_tree.try_execute().await?;
*fetch_nix = fetch_nix_handle.await.map_err(|e| e.boxed())??;
*fetch_nix = fetch_nix_handle.await.map_err(ActionError::Join)??;
move_unpacked_nix.try_execute().await?;
Ok(())
@ -119,7 +107,7 @@ impl Action for ProvisionNix {
}
#[tracing::instrument(skip_all)]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
let Self {
fetch_nix,
create_nix_tree,
@ -131,7 +119,7 @@ impl Action for ProvisionNix {
let mut fetch_nix_clone = fetch_nix.clone();
let fetch_nix_handle = tokio::task::spawn(async {
fetch_nix_clone.try_revert().await?;
Result::<_, Box<dyn std::error::Error + Send + Sync>>::Ok(fetch_nix_clone)
Result::<_, ActionError>::Ok(fetch_nix_clone)
});
if let Err(err) = create_users_and_group.try_revert().await {
@ -143,49 +131,9 @@ impl Action for ProvisionNix {
return Err(err);
}
*fetch_nix = fetch_nix_handle.await.map_err(|e| e.boxed())??;
*fetch_nix = fetch_nix_handle.await.map_err(ActionError::Join)??;
move_unpacked_nix.try_revert().await?;
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum ProvisionNixError {
#[error("Fetching Nix")]
FetchNix(
#[source]
#[from]
FetchUrlError,
),
#[error("Joining spawned async task")]
Join(
#[source]
#[from]
JoinError,
),
#[error("Creating directory")]
CreateDirectory(
#[source]
#[from]
CreateDirectoryError,
),
#[error("Creating users and group")]
CreateUsersAndGroup(
#[source]
#[from]
CreateUsersAndGroupsError,
),
#[error("Creating nix tree")]
CreateNixTree(
#[source]
#[from]
CreateNixTreeError,
),
#[error("Moving unpacked nix")]
MoveUnpackedNix(
#[source]
#[from]
MoveUnpackedNixError,
),
}

View file

@ -2,13 +2,10 @@ use std::path::{Path, PathBuf};
use tokio::process::Command;
use crate::action::StatefulAction;
use crate::action::{ActionError, StatefulAction};
use crate::execute_command;
use crate::{
action::{Action, ActionDescription},
BoxableError,
};
use crate::action::{Action, ActionDescription};
/**
Bootstrap and kickstart an APFS volume
@ -20,9 +17,7 @@ pub struct BootstrapApfsVolume {
impl BootstrapApfsVolume {
#[tracing::instrument(skip_all)]
pub async fn plan(
path: impl AsRef<Path>,
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
pub async fn plan(path: impl AsRef<Path>) -> Result<StatefulAction<Self>, ActionError> {
Ok(Self {
path: path.as_ref().to_path_buf(),
}
@ -44,7 +39,7 @@ impl Action for BootstrapApfsVolume {
#[tracing::instrument(skip_all, fields(
path = %self.path.display(),
))]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self { path } = self;
execute_command(
@ -55,7 +50,7 @@ impl Action for BootstrapApfsVolume {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| BootstrapVolumeError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
execute_command(
Command::new("launchctl")
.process_group(0)
@ -63,7 +58,7 @@ impl Action for BootstrapApfsVolume {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| BootstrapVolumeError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
Ok(())
}
@ -78,7 +73,7 @@ impl Action for BootstrapApfsVolume {
#[tracing::instrument(skip_all, fields(
path = %self.path.display(),
))]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
let Self { path } = self;
execute_command(
@ -89,7 +84,7 @@ impl Action for BootstrapApfsVolume {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| BootstrapVolumeError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
Ok(())
}

View file

@ -2,13 +2,10 @@ use std::path::{Path, PathBuf};
use tokio::process::Command;
use crate::action::StatefulAction;
use crate::action::{ActionError, StatefulAction};
use crate::execute_command;
use crate::{
action::{Action, ActionDescription},
BoxableError,
};
use crate::action::{Action, ActionDescription};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CreateApfsVolume {
@ -23,7 +20,7 @@ impl CreateApfsVolume {
disk: impl AsRef<Path>,
name: String,
case_sensitive: bool,
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
) -> Result<StatefulAction<Self>, ActionError> {
Ok(Self {
disk: disk.as_ref().to_path_buf(),
name,
@ -53,7 +50,7 @@ impl Action for CreateApfsVolume {
name = %self.name,
case_sensitive = %self.case_sensitive,
))]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self {
disk,
name,
@ -78,7 +75,7 @@ impl Action for CreateApfsVolume {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| CreateVolumeError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
Ok(())
}
@ -99,7 +96,7 @@ impl Action for CreateApfsVolume {
name = %self.name,
case_sensitive = %self.case_sensitive,
))]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
let Self {
disk: _,
name,
@ -113,7 +110,7 @@ impl Action for CreateApfsVolume {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| CreateVolumeError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
Ok(())
}

View file

@ -1,14 +1,10 @@
use crate::{
action::{
base::{CreateFile, CreateFileError, CreateOrAppendFile, CreateOrAppendFileError},
darwin::{
BootstrapApfsVolume, BootstrapVolumeError, CreateApfsVolume, CreateSyntheticObjects,
CreateSyntheticObjectsError, CreateVolumeError, EnableOwnership, EnableOwnershipError,
EncryptApfsVolume, EncryptVolumeError, UnmountApfsVolume, UnmountVolumeError,
},
Action, ActionDescription, StatefulAction,
use crate::action::{
base::{CreateFile, CreateOrAppendFile},
darwin::{
BootstrapApfsVolume, CreateApfsVolume, CreateSyntheticObjects, EnableOwnership,
EncryptApfsVolume, UnmountApfsVolume,
},
BoxableError,
Action, ActionDescription, ActionError, StatefulAction,
};
use std::{
path::{Path, PathBuf},
@ -43,7 +39,7 @@ impl CreateNixVolume {
name: String,
case_sensitive: bool,
encrypt: bool,
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
) -> Result<StatefulAction<Self>, ActionError> {
let disk = disk.as_ref();
let create_or_append_synthetic_conf = CreateOrAppendFile::plan(
"/etc/synthetic.conf",
@ -53,7 +49,7 @@ impl CreateNixVolume {
"nix\n".into(), /* The newline is required otherwise it segfaults */
)
.await
.map_err(|e| e.boxed())?;
.map_err(|e| ActionError::Child(Box::new(e)))?;
let create_synthetic_objects = CreateSyntheticObjects::plan().await?;
@ -69,7 +65,7 @@ impl CreateNixVolume {
format!("NAME=\"{name}\" /nix apfs rw,noauto,nobrowse,suid,owners"),
)
.await
.map_err(|e| e.boxed())?;
.map_err(|e| ActionError::Child(Box::new(e)))?;
let encrypt_volume = if encrypt {
Some(EncryptApfsVolume::plan(disk, &name).await?)
@ -155,7 +151,7 @@ impl Action for CreateNixVolume {
}
#[tracing::instrument(skip_all, fields(destination,))]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self {
disk: _,
name: _,
@ -193,7 +189,7 @@ impl Action for CreateNixVolume {
.stdout(std::process::Stdio::null())
.status()
.await
.map_err(|e| CreateApfsVolumeError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
if status.success() || retry_tokens == 0 {
break;
} else {
@ -218,7 +214,7 @@ impl Action for CreateNixVolume {
}
#[tracing::instrument(skip_all, fields(disk, name))]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
let Self {
disk: _,
name: _,
@ -253,25 +249,3 @@ impl Action for CreateNixVolume {
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum CreateApfsVolumeError {
#[error(transparent)]
CreateFile(#[from] CreateFileError),
#[error(transparent)]
DarwinBootstrapVolume(#[from] BootstrapVolumeError),
#[error(transparent)]
DarwinCreateSyntheticObjects(#[from] CreateSyntheticObjectsError),
#[error(transparent)]
DarwinCreateVolume(#[from] CreateVolumeError),
#[error(transparent)]
DarwinEnableOwnership(#[from] EnableOwnershipError),
#[error(transparent)]
DarwinEncryptVolume(#[from] EncryptVolumeError),
#[error(transparent)]
DarwinUnmountVolume(#[from] UnmountVolumeError),
#[error(transparent)]
CreateOrAppendFile(#[from] CreateOrAppendFileError),
#[error("Failed to execute command")]
Command(#[source] std::io::Error),
}

View file

@ -2,7 +2,7 @@ use tokio::process::Command;
use crate::execute_command;
use crate::action::{Action, ActionDescription, StatefulAction};
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
/// Create the synthetic objects defined in `/etc/syntethic.conf`
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
@ -10,7 +10,7 @@ pub struct CreateSyntheticObjects;
impl CreateSyntheticObjects {
#[tracing::instrument(skip_all)]
pub async fn plan() -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
pub async fn plan() -> Result<StatefulAction<Self>, ActionError> {
Ok(Self.into())
}
}
@ -30,7 +30,7 @@ impl Action for CreateSyntheticObjects {
}
#[tracing::instrument(skip_all, fields())]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
// Yup we literally call both and ignore the error! Reasoning: https://github.com/NixOS/nix/blob/95331cb9c99151cbd790ceb6ddaf49fc1c0da4b3/scripts/create-darwin-volume.sh#L261
execute_command(
Command::new("/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util")
@ -60,7 +60,7 @@ impl Action for CreateSyntheticObjects {
}
#[tracing::instrument(skip_all, fields())]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
// Yup we literally call both and ignore the error! Reasoning: https://github.com/NixOS/nix/blob/95331cb9c99151cbd790ceb6ddaf49fc1c0da4b3/scripts/create-darwin-volume.sh#L261
execute_command(
Command::new("/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util")

View file

@ -3,14 +3,11 @@ use std::path::{Path, PathBuf};
use tokio::process::Command;
use crate::action::StatefulAction;
use crate::action::{ActionError, StatefulAction};
use crate::execute_command;
use crate::action::{Action, ActionDescription};
use crate::os::darwin::DiskUtilOutput;
use crate::{
action::{Action, ActionDescription},
BoxableError,
};
/**
Enable ownership on a volume
@ -22,9 +19,7 @@ pub struct EnableOwnership {
impl EnableOwnership {
#[tracing::instrument(skip_all)]
pub async fn plan(
path: impl AsRef<Path>,
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
pub async fn plan(path: impl AsRef<Path>) -> Result<StatefulAction<Self>, ActionError> {
Ok(Self {
path: path.as_ref().to_path_buf(),
}
@ -46,7 +41,7 @@ impl Action for EnableOwnership {
#[tracing::instrument(skip_all, fields(
path = %self.path.display(),
))]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self { path } = self;
let should_enable_ownership = {
@ -57,7 +52,8 @@ impl Action for EnableOwnership {
.arg(&path)
.stdin(std::process::Stdio::null()),
)
.await?
.await
.map_err(ActionError::Command)?
.stdout;
let the_plist: DiskUtilOutput = plist::from_reader(Cursor::new(buf)).unwrap();
@ -73,7 +69,7 @@ impl Action for EnableOwnership {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| EnableOwnershipError::Command(e).boxed())?;
.map_err(ActionError::Command)?;
}
Ok(())
@ -86,7 +82,7 @@ impl Action for EnableOwnership {
#[tracing::instrument(skip_all, fields(
path = %self.path.display(),
))]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
// noop
Ok(())
}

View file

@ -1,5 +1,7 @@
use crate::{
action::{darwin::NIX_VOLUME_MOUNTD_DEST, Action, ActionDescription, StatefulAction},
action::{
darwin::NIX_VOLUME_MOUNTD_DEST, Action, ActionDescription, ActionError, StatefulAction,
},
execute_command,
};
use rand::Rng;
@ -20,7 +22,7 @@ impl EncryptApfsVolume {
pub async fn plan(
disk: impl AsRef<Path>,
name: impl AsRef<str>,
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
) -> Result<StatefulAction<Self>, ActionError> {
let name = name.as_ref().to_owned();
Ok(Self {
name,
@ -48,7 +50,7 @@ impl Action for EncryptApfsVolume {
#[tracing::instrument(skip_all, fields(
disk = %self.disk.display(),
))]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self { disk, name } = self;
// Generate a random password.
@ -69,7 +71,9 @@ impl Action for EncryptApfsVolume {
let disk_str = disk.to_str().expect("Could not turn disk into string"); /* Should not reasonably ever fail */
execute_command(Command::new("/usr/sbin/diskutil").arg("mount").arg(&name)).await?;
execute_command(Command::new("/usr/sbin/diskutil").arg("mount").arg(&name))
.await
.map_err(ActionError::Command)?;
// Add the password to the user keychain so they can unlock it later.
execute_command(
@ -99,7 +103,8 @@ impl Action for EncryptApfsVolume {
"/Library/Keychains/System.keychain",
]),
)
.await?;
.await
.map_err(ActionError::Command)?;
// Encrypt the mounted volume
execute_command(Command::new("/usr/sbin/diskutil").process_group(0).args([
@ -111,7 +116,8 @@ impl Action for EncryptApfsVolume {
"-passphrase",
password.as_str(),
]))
.await?;
.await
.map_err(ActionError::Command)?;
execute_command(
Command::new("/usr/sbin/diskutil")
@ -120,7 +126,8 @@ impl Action for EncryptApfsVolume {
.arg("force")
.arg(&name),
)
.await?;
.await
.map_err(ActionError::Command)?;
Ok(())
}
@ -138,7 +145,7 @@ impl Action for EncryptApfsVolume {
#[tracing::instrument(skip_all, fields(
disk = %self.disk.display(),
))]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
let Self { disk, name } = self;
let disk_str = disk.to_str().expect("Could not turn disk into string"); /* Should not reasonably ever fail */
@ -162,14 +169,9 @@ impl Action for EncryptApfsVolume {
.as_str(),
]),
)
.await?;
.await
.map_err(ActionError::Command)?;
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum EncryptVolumeError {
#[error("Failed to execute command")]
Command(#[source] std::io::Error),
}

View file

@ -1,12 +1,9 @@
use tokio::process::Command;
use crate::action::StatefulAction;
use crate::action::{ActionError, StatefulAction};
use crate::execute_command;
use crate::{
action::{Action, ActionDescription},
BoxableError,
};
use crate::action::{Action, ActionDescription};
/**
Kickstart a `launchctl` service
@ -18,9 +15,7 @@ pub struct KickstartLaunchctlService {
impl KickstartLaunchctlService {
#[tracing::instrument(skip_all)]
pub async fn plan(
unit: String,
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
pub async fn plan(unit: String) -> Result<StatefulAction<Self>, ActionError> {
Ok(Self { unit }.into())
}
}
@ -40,7 +35,7 @@ impl Action for KickstartLaunchctlService {
#[tracing::instrument(skip_all, fields(
unit = %self.unit,
))]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self { unit } = self;
execute_command(
@ -52,7 +47,7 @@ impl Action for KickstartLaunchctlService {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| KickstartLaunchctlServiceError::Command(e).boxed())?;
.map_err(ActionError::Command)?;
Ok(())
}
@ -64,14 +59,8 @@ impl Action for KickstartLaunchctlService {
#[tracing::instrument(skip_all, fields(
unit = %self.unit,
))]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
// noop
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum KickstartLaunchctlServiceError {
#[error("Failed to execute command")]
Command(#[source] std::io::Error),
}

View file

@ -12,9 +12,9 @@ mod unmount_apfs_volume;
pub use bootstrap_apfs_volume::{BootstrapApfsVolume, BootstrapVolumeError};
pub use create_apfs_volume::{CreateApfsVolume, CreateVolumeError};
pub use create_nix_volume::{CreateApfsVolumeError, CreateNixVolume, NIX_VOLUME_MOUNTD_DEST};
pub use create_nix_volume::{CreateNixVolume, NIX_VOLUME_MOUNTD_DEST};
pub use create_synthetic_objects::{CreateSyntheticObjects, CreateSyntheticObjectsError};
pub use enable_ownership::{EnableOwnership, EnableOwnershipError};
pub use encrypt_apfs_volume::{EncryptApfsVolume, EncryptVolumeError};
pub use kickstart_launchctl_service::{KickstartLaunchctlService, KickstartLaunchctlServiceError};
pub use unmount_apfs_volume::{UnmountApfsVolume, UnmountVolumeError};
pub use encrypt_apfs_volume::EncryptApfsVolume;
pub use kickstart_launchctl_service::KickstartLaunchctlService;
pub use unmount_apfs_volume::UnmountApfsVolume;

View file

@ -2,13 +2,10 @@ use std::path::{Path, PathBuf};
use tokio::process::Command;
use crate::action::StatefulAction;
use crate::action::{ActionError, StatefulAction};
use crate::execute_command;
use crate::{
action::{Action, ActionDescription},
BoxableError,
};
use crate::action::{Action, ActionDescription};
/**
Unmount an APFS volume
@ -24,7 +21,7 @@ impl UnmountApfsVolume {
pub async fn plan(
disk: impl AsRef<Path>,
name: String,
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
) -> Result<StatefulAction<Self>, ActionError> {
let disk = disk.as_ref().to_owned();
Ok(Self { disk, name }.into())
}
@ -45,7 +42,7 @@ impl Action for UnmountApfsVolume {
disk = %self.disk.display(),
name = %self.name,
))]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self { disk: _, name } = self;
execute_command(
@ -56,7 +53,7 @@ impl Action for UnmountApfsVolume {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| UnmountVolumeError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
Ok(())
}
@ -69,7 +66,7 @@ impl Action for UnmountApfsVolume {
disk = %self.disk.display(),
name = %self.name,
))]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
let Self { disk: _, name } = self;
execute_command(
@ -80,14 +77,8 @@ impl Action for UnmountApfsVolume {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| UnmountVolumeError::Command(e).boxed())?;
.map_err(|e| ActionError::Command(e))?;
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum UnmountVolumeError {
#[error("Failed to execute command")]
Command(#[source] std::io::Error),
}

View file

@ -4,13 +4,10 @@ use target_lexicon::OperatingSystem;
use tokio::fs::remove_file;
use tokio::process::Command;
use crate::action::StatefulAction;
use crate::action::{ActionError, StatefulAction};
use crate::execute_command;
use crate::{
action::{Action, ActionDescription},
BoxableError,
};
use crate::action::{Action, ActionDescription};
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";
@ -26,7 +23,7 @@ pub struct ConfigureNixDaemonService {}
impl ConfigureNixDaemonService {
#[tracing::instrument(skip_all)]
pub async fn plan() -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
pub async fn plan() -> Result<StatefulAction<Self>, ActionError> {
match OperatingSystem::host() {
OperatingSystem::MacOSX {
major: _,
@ -36,7 +33,9 @@ impl ConfigureNixDaemonService {
| OperatingSystem::Darwin => (),
_ => {
if !Path::new("/run/systemd/system").exists() {
return Err(ConfigureNixDaemonServiceError::InitNotSupported.boxed());
return Err(ActionError::Custom(Box::new(
ConfigureNixDaemonServiceError::InitNotSupported,
)));
}
},
};
@ -64,7 +63,7 @@ impl Action for ConfigureNixDaemonService {
}
#[tracing::instrument(skip_all)]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self {} = self;
match OperatingSystem::host() {
@ -78,12 +77,11 @@ impl Action for ConfigureNixDaemonService {
tokio::fs::copy(src.clone(), DARWIN_NIX_DAEMON_DEST)
.await
.map_err(|e| {
ConfigureNixDaemonServiceError::Copy(
ActionError::Copy(
src.to_path_buf(),
PathBuf::from(DARWIN_NIX_DAEMON_DEST),
e,
)
.boxed()
})?;
execute_command(
@ -94,19 +92,18 @@ impl Action for ConfigureNixDaemonService {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
.map_err(ActionError::Command)?;
},
_ => {
tracing::trace!(src = TMPFILES_SRC, dest = TMPFILES_DEST, "Symlinking");
tokio::fs::symlink(TMPFILES_SRC, TMPFILES_DEST)
.await
.map_err(|e| {
ConfigureNixDaemonServiceError::Symlink(
ActionError::Symlink(
PathBuf::from(TMPFILES_SRC),
PathBuf::from(TMPFILES_DEST),
e,
)
.boxed()
})?;
execute_command(
@ -117,7 +114,7 @@ impl Action for ConfigureNixDaemonService {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
.map_err(ActionError::Command)?;
execute_command(
Command::new("systemctl")
@ -127,7 +124,7 @@ impl Action for ConfigureNixDaemonService {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
.map_err(ActionError::Command)?;
execute_command(
Command::new("systemctl")
@ -137,7 +134,7 @@ impl Action for ConfigureNixDaemonService {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
.map_err(ActionError::Command)?;
execute_command(
Command::new("systemctl")
@ -146,7 +143,7 @@ impl Action for ConfigureNixDaemonService {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
.map_err(ActionError::Command)?;
execute_command(
Command::new("systemctl")
@ -156,7 +153,7 @@ impl Action for ConfigureNixDaemonService {
.arg("nix-daemon.socket"),
)
.await
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
.map_err(ActionError::Command)?;
},
};
@ -176,7 +173,7 @@ impl Action for ConfigureNixDaemonService {
}
#[tracing::instrument(skip_all)]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
match OperatingSystem::host() {
OperatingSystem::MacOSX {
major: _,
@ -191,7 +188,7 @@ impl Action for ConfigureNixDaemonService {
.arg(DARWIN_NIX_DAEMON_DEST),
)
.await
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
.map_err(ActionError::Command)?;
},
_ => {
// We separate stop and disable (instead of using `--now`) to avoid cases where the service isn't started, but is enabled.
@ -209,7 +206,7 @@ impl Action for ConfigureNixDaemonService {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
.map_err(ActionError::Command)?;
}
if socket_is_enabled {
@ -220,7 +217,7 @@ impl Action for ConfigureNixDaemonService {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
.map_err(ActionError::Command)?;
}
if service_is_active {
@ -231,7 +228,7 @@ impl Action for ConfigureNixDaemonService {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
.map_err(ActionError::Command)?;
}
if service_is_enabled {
@ -242,7 +239,7 @@ impl Action for ConfigureNixDaemonService {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
.map_err(ActionError::Command)?;
}
execute_command(
@ -253,12 +250,11 @@ impl Action for ConfigureNixDaemonService {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
.map_err(ActionError::Command)?;
remove_file(TMPFILES_DEST).await.map_err(|e| {
ConfigureNixDaemonServiceError::RemoveFile(PathBuf::from(TMPFILES_DEST), e)
.boxed()
})?;
remove_file(TMPFILES_DEST)
.await
.map_err(|e| ActionError::Remove(PathBuf::from(TMPFILES_DEST), e))?;
execute_command(
Command::new("systemctl")
@ -267,7 +263,7 @@ impl Action for ConfigureNixDaemonService {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
.map_err(ActionError::Command)?;
},
};
@ -277,34 +273,17 @@ impl Action for ConfigureNixDaemonService {
#[derive(Debug, thiserror::Error)]
pub enum ConfigureNixDaemonServiceError {
#[error("Symlinking from `{0}` to `{1}`")]
Symlink(
std::path::PathBuf,
std::path::PathBuf,
#[source] std::io::Error,
),
#[error("Set mode `{0}` on `{1}`")]
SetPermissions(u32, std::path::PathBuf, #[source] std::io::Error),
#[error("Command failed to execute")]
Command(#[source] std::io::Error),
#[error("Remove file `{0}`")]
RemoveFile(std::path::PathBuf, #[source] std::io::Error),
#[error("Copying file `{0}` to `{1}`")]
Copy(
std::path::PathBuf,
std::path::PathBuf,
#[source] std::io::Error,
),
#[error("No supported init system found")]
InitNotSupported,
}
async fn is_active(unit: &str) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
async fn is_active(unit: &str) -> Result<bool, ActionError> {
let output = Command::new("systemctl")
.arg("is-active")
.arg(unit)
.output()
.await?;
.await
.map_err(ActionError::Command)?;
if String::from_utf8(output.stdout)?.starts_with("active") {
tracing::trace!(%unit, "Is active");
Ok(true)
@ -314,12 +293,13 @@ async fn is_active(unit: &str) -> Result<bool, Box<dyn std::error::Error + Send
}
}
async fn is_enabled(unit: &str) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
async fn is_enabled(unit: &str) -> Result<bool, ActionError> {
let output = Command::new("systemctl")
.arg("is-enabled")
.arg(unit)
.output()
.await?;
.await
.map_err(ActionError::Command)?;
let stdout = String::from_utf8(output.stdout)?;
if stdout.starts_with("enabled") || stdout.starts_with("linked") {
tracing::trace!(%unit, "Is enabled");

View file

@ -1,12 +1,9 @@
use tokio::process::Command;
use crate::action::{ActionState, StatefulAction};
use crate::action::{ActionError, ActionState, StatefulAction};
use crate::execute_command;
use crate::{
action::{Action, ActionDescription},
BoxableError,
};
use crate::action::{Action, ActionDescription};
/**
Start a given systemd unit
@ -18,9 +15,7 @@ pub struct StartSystemdUnit {
impl StartSystemdUnit {
#[tracing::instrument(skip_all)]
pub async fn plan(
unit: impl AsRef<str>,
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
pub async fn plan(unit: impl AsRef<str>) -> Result<StatefulAction<Self>, ActionError> {
Ok(StatefulAction {
action: Self {
unit: unit.as_ref().to_string(),
@ -44,7 +39,7 @@ impl Action for StartSystemdUnit {
#[tracing::instrument(skip_all, fields(
unit = %self.unit,
))]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
let Self { unit, .. } = self;
// TODO(@Hoverbear): Handle proxy vars
@ -57,7 +52,7 @@ impl Action for StartSystemdUnit {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| StartSystemdUnitError::Command(e).boxed())?;
.map_err(|e| ActionError::Custom(Box::new(StartSystemdUnitError::Command(e))))?;
Ok(())
}
@ -72,7 +67,7 @@ impl Action for StartSystemdUnit {
#[tracing::instrument(skip_all, fields(
unit = %self.unit,
))]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
let Self { unit, .. } = self;
execute_command(
@ -83,7 +78,7 @@ impl Action for StartSystemdUnit {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| StartSystemdUnitError::Command(e).boxed())?;
.map_err(|e| ActionError::Custom(Box::new(StartSystemdUnitError::Command(e))))?;
// We do both to avoid an error doing `disable --now` if the user did stop it already somehow.
execute_command(
@ -94,7 +89,7 @@ impl Action for StartSystemdUnit {
.stdin(std::process::Stdio::null()),
)
.await
.map_err(|e| StartSystemdUnitError::Command(e).boxed())?;
.map_err(|e| ActionError::Custom(Box::new(StartSystemdUnitError::Command(e))))?;
Ok(())
}

View file

@ -50,7 +50,7 @@ use harmonic::{
InstallPlan,
settings::{CommonSettings, InstallSettingsError},
planner::{Planner, PlannerError, linux::SteamDeck},
action::{Action, StatefulAction, ActionDescription},
action::{Action, ActionError, StatefulAction, ActionDescription},
};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
@ -59,7 +59,7 @@ pub struct MyAction {}
impl MyAction {
#[tracing::instrument(skip_all)]
pub async fn plan() -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
pub async fn plan() -> Result<StatefulAction<Self>, ActionError> {
Ok(Self {}.into())
}
}
@ -79,7 +79,7 @@ impl Action for MyAction {
#[tracing::instrument(skip_all, fields(
// Tracing fields...
))]
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn execute(&mut self) -> Result<(), ActionError> {
// Execute steps ...
Ok(())
}
@ -91,7 +91,7 @@ impl Action for MyAction {
#[tracing::instrument(skip_all, fields(
// Tracing fields...
))]
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
async fn revert(&mut self) -> Result<(), ActionError> {
// Revert steps...
Ok(())
}
@ -158,6 +158,8 @@ pub mod linux;
mod stateful;
pub use stateful::{ActionState, StatefulAction};
use std::error::Error;
use tokio::task::JoinError;
/// An action which can be reverted or completed, with an action state
///
@ -186,13 +188,13 @@ pub trait Action: Send + Sync + std::fmt::Debug + dyn_clone::DynClone {
/// If this action calls sub-[`Action`]s, care should be taken to call [`try_execute`][StatefulAction::try_execute], not [`execute`][Action::execute], so that [`ActionState`] is handled correctly and tracing is done.
///
/// This is called by [`InstallPlan::install`](crate::InstallPlan::install) through [`StatefulAction::try_execute`] which handles tracing as well as if the action needs to execute based on its `action_state`.
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
async fn execute(&mut self) -> Result<(), ActionError>;
/// Perform any revert steps
///
/// If this action calls sub-[`Action`]s, care should be taken to call [`try_revert`][StatefulAction::try_revert], not [`revert`][Action::revert], so that [`ActionState`] is handled correctly and tracing is done.
///
/// /// This is called by [`InstallPlan::uninstall`](crate::InstallPlan::uninstall) through [`StatefulAction::try_revert`] which handles tracing as well as if the action needs to revert based on its `action_state`.
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
async fn revert(&mut self) -> Result<(), ActionError>;
fn stateful(self) -> StatefulAction<Self>
where
@ -203,7 +205,7 @@ pub trait Action: Send + Sync + std::fmt::Debug + dyn_clone::DynClone {
state: ActionState::Uncompleted,
}
}
// They should also have an `async fn plan(args...) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>>;`
// They should also have an `async fn plan(args...) -> Result<StatefulAction<Self>, ActionError>;`
}
dyn_clone::clone_trait_object!(Action);
@ -225,3 +227,85 @@ impl ActionDescription {
}
}
}
/// An error occurring during an action
#[derive(thiserror::Error, Debug)]
pub enum ActionError {
/// A custom error
#[error(transparent)]
Custom(Box<dyn std::error::Error + Send + Sync>),
/// A child error
#[error(transparent)]
Child(#[from] Box<ActionError>),
/// Several child errors
#[error("Multiple errors: {}", .0.iter().map(|v| {
if let Some(source) = v.source() {
format!("{v} ({source})")
} else {
format!("{v}")
}
}).collect::<Vec<_>>().join(" & "))]
Children(Vec<Box<ActionError>>),
/// The path already exists
#[error("Path exists `{0}`")]
Exists(std::path::PathBuf),
#[error("Getting metadata for {0}`")]
GettingMetadata(std::path::PathBuf, #[source] std::io::Error),
#[error("Creating directory `{0}`")]
CreateDirectory(std::path::PathBuf, #[source] std::io::Error),
#[error("Symlinking from `{0}` to `{1}`")]
Symlink(
std::path::PathBuf,
std::path::PathBuf,
#[source] std::io::Error,
),
#[error("Set mode `{0}` on `{1}`")]
SetPermissions(u32, std::path::PathBuf, #[source] std::io::Error),
#[error("Remove file `{0}`")]
Remove(std::path::PathBuf, #[source] std::io::Error),
#[error("Copying file `{0}` to `{1}`")]
Copy(
std::path::PathBuf,
std::path::PathBuf,
#[source] std::io::Error,
),
#[error("Rename `{0}` to `{1}`")]
Rename(
std::path::PathBuf,
std::path::PathBuf,
#[source] std::io::Error,
),
#[error("Remove path `{0}`")]
Read(std::path::PathBuf, #[source] std::io::Error),
#[error("Open path `{0}`")]
Open(std::path::PathBuf, #[source] std::io::Error),
#[error("Write path `{0}`")]
Write(std::path::PathBuf, #[source] std::io::Error),
#[error("Seek path `{0}`")]
Seek(std::path::PathBuf, #[source] std::io::Error),
#[error("Getting uid for user `{0}`")]
UserId(String, #[source] nix::errno::Errno),
#[error("Getting user `{0}`")]
NoUser(String),
#[error("Getting gid for group `{0}`")]
GroupId(String, #[source] nix::errno::Errno),
#[error("Getting group `{0}`")]
NoGroup(String),
#[error("Chowning path `{0}`")]
Chown(std::path::PathBuf, #[source] nix::errno::Errno),
/// Failed to execute command
#[error("Failed to execute command")]
Command(#[source] std::io::Error),
#[error("Joining spawned async task")]
Join(
#[source]
#[from]
JoinError,
),
#[error("String from UTF-8 error")]
FromUtf8(
#[source]
#[from]
std::string::FromUtf8Error,
),
}

View file

@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};
use super::{Action, ActionDescription};
use super::{Action, ActionDescription, ActionError};
/// A wrapper around an [`Action`](crate::action::Action) which tracks the [`ActionState`] and
/// handles some tracing output
@ -44,7 +44,7 @@ impl StatefulAction<Box<dyn Action>> {
/// Perform any execution steps
///
/// You should prefer this ([`try_execute`][StatefulAction::try_execute]) over [`execute`][Action::execute] as it handles [`ActionState`] and does tracing
pub async fn try_execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
pub async fn try_execute(&mut self) -> Result<(), ActionError> {
match self.state {
ActionState::Completed => {
tracing::trace!(
@ -70,7 +70,7 @@ impl StatefulAction<Box<dyn Action>> {
/// Perform any revert steps
///
/// You should prefer this ([`try_revert`][StatefulAction::try_revert]) over [`revert`][Action::revert] as it handles [`ActionState`] and does tracing
pub async fn try_revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
pub async fn try_revert(&mut self) -> Result<(), ActionError> {
match self.state {
ActionState::Uncompleted => {
tracing::trace!(
@ -129,7 +129,7 @@ where
/// Perform any execution steps
///
/// You should prefer this ([`try_execute`][StatefulAction::try_execute]) over [`execute`][Action::execute] as it handles [`ActionState`] and does tracing
pub async fn try_execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
pub async fn try_execute(&mut self) -> Result<(), ActionError> {
match self.state {
ActionState::Completed => {
tracing::trace!(
@ -155,7 +155,7 @@ where
/// Perform any revert steps
///
/// You should prefer this ([`try_revert`][StatefulAction::try_revert]) over [`revert`][Action::revert] as it handles [`ActionState`] and does tracing
pub async fn try_revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
pub async fn try_revert(&mut self) -> Result<(), ActionError> {
match self.state {
ActionState::Uncompleted => {
tracing::trace!(

View file

@ -1,6 +1,6 @@
use std::path::PathBuf;
use crate::{planner::PlannerError, settings::InstallSettingsError};
use crate::{action::ActionError, planner::PlannerError, settings::InstallSettingsError};
/// An error occurring during a call defined in this crate
#[derive(thiserror::Error, Debug)]
@ -10,7 +10,7 @@ pub enum HarmonicError {
Action(
#[source]
#[from]
Box<dyn std::error::Error + Send + Sync>,
ActionError,
),
/// An error while writing the [`InstallPlan`](crate::InstallPlan)
#[error("Recording install receipt")]

View file

@ -112,14 +112,3 @@ fn set_env(k: impl AsRef<OsStr>, v: impl AsRef<OsStr>) {
tracing::trace!("Setting env");
std::env::set_var(k.as_ref(), v.as_ref());
}
trait BoxableError: std::error::Error + Send + Sync {
fn boxed(self) -> Box<dyn std::error::Error + Send + Sync>
where
Self: Sized + 'static,
{
Box::new(self)
}
}
impl<E> BoxableError for E where E: std::error::Error + Send + Sized + Sync {}

View file

@ -80,7 +80,9 @@ pub mod linux;
use std::collections::HashMap;
use crate::{
action::StatefulAction, settings::InstallSettingsError, Action, HarmonicError, InstallPlan,
action::{ActionError, StatefulAction},
settings::InstallSettingsError,
Action, HarmonicError, InstallPlan,
};
/// Something which can be used to plan out an [`InstallPlan`]
@ -165,7 +167,11 @@ pub enum PlannerError {
UnsupportedArchitecture(target_lexicon::Triple),
/// Error executing action
#[error("Error executing action")]
Action(#[source] Box<dyn std::error::Error + Send + Sync>),
Action(
#[source]
#[from]
ActionError,
),
/// An [`InstallSettingsError`]
#[error(transparent)]
InstallSettings(#[from] InstallSettingsError),