Add skipped option to ActionState (#74)

* Make plans versioned

* Delint

* speeeeeeeeling

* remove file that was dead

* Flesh out docs and improve public API

* Speeling

* Fixups

* Fix doctests

* Do a better job with actionstate

* Add some more docs

* Fix doctest

* Make CLI stuff optional

* Touchup

* Speeling

* add skipped to ActionState
This commit is contained in:
Ana Hobden 2022-12-02 06:58:25 -08:00 committed by GitHub
parent 4134f7af02
commit 017bcaf1f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 99 additions and 60 deletions

View file

@ -49,7 +49,7 @@ impl CreateDirectory {
path.display(), path.display(),
); );
// TODO: Validate owner/group... // TODO: Validate owner/group...
ActionState::Completed ActionState::Skipped
} else { } else {
return Err(CreateDirectoryError::Exists(std::io::Error::new( return Err(CreateDirectoryError::Exists(std::io::Error::new(
std::io::ErrorKind::AlreadyExists, std::io::ErrorKind::AlreadyExists,

View file

@ -6,7 +6,7 @@ use crate::action::StatefulAction;
use crate::execute_command; use crate::execute_command;
use crate::{ use crate::{
action::{Action, ActionDescription, ActionState}, action::{Action, ActionDescription},
BoxableError, BoxableError,
}; };
@ -15,7 +15,6 @@ pub struct CreateApfsVolume {
disk: PathBuf, disk: PathBuf,
name: String, name: String,
case_sensitive: bool, case_sensitive: bool,
action_state: ActionState,
} }
impl CreateApfsVolume { impl CreateApfsVolume {
@ -29,7 +28,6 @@ impl CreateApfsVolume {
disk: disk.as_ref().to_path_buf(), disk: disk.as_ref().to_path_buf(),
name, name,
case_sensitive, case_sensitive,
action_state: ActionState::Uncompleted,
} }
.into()) .into())
} }
@ -60,7 +58,6 @@ impl Action for CreateApfsVolume {
disk, disk,
name, name,
case_sensitive, case_sensitive,
action_state: _,
} = self; } = self;
execute_command( execute_command(
@ -107,7 +104,6 @@ impl Action for CreateApfsVolume {
disk: _, disk: _,
name, name,
case_sensitive: _, case_sensitive: _,
action_state: _,
} = self; } = self;
execute_command( execute_command(

View file

@ -25,53 +25,73 @@ where
impl StatefulAction<Box<dyn Action>> { impl StatefulAction<Box<dyn Action>> {
/// A description of what this action would do during execution /// A description of what this action would do during execution
pub fn describe_execute(&self) -> Vec<ActionDescription> { pub fn describe_execute(&self) -> Vec<ActionDescription> {
if self.state == ActionState::Completed { match self.state {
return vec![]; ActionState::Uncompleted | ActionState::Skipped => {
vec![]
},
_ => self.action.execute_description(),
} }
return self.action.execute_description();
} }
/// A description of what this action would do during revert /// A description of what this action would do during revert
pub fn describe_revert(&self) -> Vec<ActionDescription> { pub fn describe_revert(&self) -> Vec<ActionDescription> {
if self.state == ActionState::Uncompleted { match self.state {
return vec![]; ActionState::Completed | ActionState::Skipped => {
vec![]
},
_ => self.action.revert_description(),
} }
return self.action.revert_description();
} }
/// Perform any execution steps /// Perform any execution steps
/// ///
/// You should prefer this ([`try_execute`][StatefulAction::try_execute]) over [`execute`][Action::execute] as it handles [`ActionState`] and does tracing /// 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<(), Box<dyn std::error::Error + Send + Sync>> {
if self.state == ActionState::Completed { match self.state {
ActionState::Completed => {
tracing::trace!( tracing::trace!(
"Completed: (Already done) {}", "Completed: (Already done) {}",
self.action.tracing_synopsis() self.action.tracing_synopsis()
); );
return Ok(()); Ok(())
} },
ActionState::Skipped => {
tracing::trace!("Skipped: {}", self.action.tracing_synopsis());
Ok(())
},
_ => {
self.state = ActionState::Progress; self.state = ActionState::Progress;
tracing::debug!("Executing: {}", self.action.tracing_synopsis()); tracing::debug!("Executing: {}", self.action.tracing_synopsis());
self.action.execute().await?; self.action.execute().await?;
self.state = ActionState::Completed; self.state = ActionState::Completed;
tracing::debug!("Completed: {}", self.action.tracing_synopsis()); tracing::debug!("Completed: {}", self.action.tracing_synopsis());
Ok(()) Ok(())
},
}
} }
/// Perform any revert steps /// Perform any revert steps
/// ///
/// You should prefer this ([`try_revert`][StatefulAction::try_revert]) over [`revert`][Action::revert] as it handles [`ActionState`] and does tracing /// 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<(), Box<dyn std::error::Error + Send + Sync>> {
if self.state == ActionState::Uncompleted { match self.state {
ActionState::Uncompleted => {
tracing::trace!( tracing::trace!(
"Reverted: (Already done) {}", "Reverted: (Already done) {}",
self.action.tracing_synopsis() self.action.tracing_synopsis()
); );
return Ok(()); Ok(())
} },
ActionState::Skipped => {
tracing::trace!("Skipped: {}", self.action.tracing_synopsis());
Ok(())
},
_ => {
self.state = ActionState::Progress; self.state = ActionState::Progress;
tracing::debug!("Reverting: {}", self.action.tracing_synopsis()); tracing::debug!("Reverting: {}", self.action.tracing_synopsis());
self.action.revert().await?; self.action.revert().await?;
tracing::debug!("Reverted: {}", self.action.tracing_synopsis()); tracing::debug!("Reverted: {}", self.action.tracing_synopsis());
self.state = ActionState::Uncompleted; self.state = ActionState::Uncompleted;
Ok(()) Ok(())
},
}
} }
} }
@ -110,37 +130,53 @@ where
/// ///
/// You should prefer this ([`try_execute`][StatefulAction::try_execute]) over [`execute`][Action::execute] as it handles [`ActionState`] and does tracing /// 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<(), Box<dyn std::error::Error + Send + Sync>> {
if self.state == ActionState::Completed { match self.state {
ActionState::Completed => {
tracing::trace!( tracing::trace!(
"Completed: (Already done) {}", "Completed: (Already done) {}",
self.action.tracing_synopsis() self.action.tracing_synopsis()
); );
return Ok(()); Ok(())
} },
ActionState::Skipped => {
tracing::trace!("Skipped: {}", self.action.tracing_synopsis());
Ok(())
},
_ => {
self.state = ActionState::Progress; self.state = ActionState::Progress;
tracing::debug!("Executing: {}", self.action.tracing_synopsis()); tracing::debug!("Executing: {}", self.action.tracing_synopsis());
self.action.execute().await?; self.action.execute().await?;
self.state = ActionState::Completed; self.state = ActionState::Completed;
tracing::debug!("Completed: {}", self.action.tracing_synopsis()); tracing::debug!("Completed: {}", self.action.tracing_synopsis());
Ok(()) Ok(())
},
}
} }
/// Perform any revert steps /// Perform any revert steps
/// ///
/// You should prefer this ([`try_revert`][StatefulAction::try_revert]) over [`revert`][Action::revert] as it handles [`ActionState`] and does tracing /// 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<(), Box<dyn std::error::Error + Send + Sync>> {
if self.state == ActionState::Uncompleted { match self.state {
ActionState::Uncompleted => {
tracing::trace!( tracing::trace!(
"Reverted: (Already done) {}", "Reverted: (Already done) {}",
self.action.tracing_synopsis() self.action.tracing_synopsis()
); );
return Ok(()); Ok(())
} },
ActionState::Skipped => {
tracing::trace!("Skipped: {}", self.action.tracing_synopsis());
Ok(())
},
_ => {
self.state = ActionState::Progress; self.state = ActionState::Progress;
tracing::debug!("Reverting: {}", self.action.tracing_synopsis()); tracing::debug!("Reverting: {}", self.action.tracing_synopsis());
self.action.revert().await?; self.action.revert().await?;
tracing::debug!("Reverted: {}", self.action.tracing_synopsis()); tracing::debug!("Reverted: {}", self.action.tracing_synopsis());
self.state = ActionState::Uncompleted; self.state = ActionState::Uncompleted;
Ok(()) Ok(())
},
}
} }
} }
@ -165,4 +201,11 @@ pub enum ActionState {
on [`InstallPlan::uninstall`](crate::InstallPlan::uninstall) and executed on [`InstallPlan::install`](crate::InstallPlan::install) on [`InstallPlan::uninstall`](crate::InstallPlan::uninstall) and executed on [`InstallPlan::install`](crate::InstallPlan::install)
*/ */
Uncompleted, Uncompleted,
/**
If [`Skipped`](ActionState::Skipped) an [`Action`](crate::action::Action) will be skipped
on [`InstallPlan::install`](crate::InstallPlan::install) and [`InstallPlan::uninstall`](crate::InstallPlan::uninstall)
Typically this is used by actions which detect they are already completed in their `plan` phase.
*/
Skipped,
} }