Skip to content

Commit

Permalink
feat(core): prepare the skeleton of coffee nurse command
Browse files Browse the repository at this point in the history
Signed-off-by: Tarek <[email protected]>
  • Loading branch information
tareknaser committed Sep 19, 2023
1 parent 35f56c9 commit 93eb17d
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 22 deletions.
26 changes: 21 additions & 5 deletions coffee_cmd/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use radicle_term as term;
use coffee_core::coffee::CoffeeManager;
use coffee_lib::errors::CoffeeError;
use coffee_lib::plugin_manager::PluginManager;
use coffee_lib::types::response::UpgradeStatus;
use coffee_lib::types::response::{NurseStatus, UpgradeStatus};

use crate::cmd::CoffeeArgs;
use crate::cmd::CoffeeCommand;
Expand Down Expand Up @@ -123,10 +123,26 @@ async fn main() -> Result<(), CoffeeError> {
}
Err(err) => Err(err),
},
CoffeeCommand::Nurse {} => {
term::info!("Nurse command is not implemented");
Ok(())
}
CoffeeCommand::Nurse {} => match coffee.nurse().await {
Ok(val) => {
match val.status {
NurseStatus::Sane => {
term::success!("coffee configuration is not corrupt!")
}
NurseStatus::RepositoryLocallyAbsent => {
term::success!("A repository was locally absent");
}
NurseStatus::RepositoryLocallyCorrupt => {
term::success!("A repository was locally corrupt");
}
NurseStatus::RepositoryMissingInConfiguration => {
term::success!("A repository was missing in the configuration");
}
}
Ok(())
}
Err(err) => Err(err),
},
};

if let Err(err) = result {
Expand Down
2 changes: 1 addition & 1 deletion coffee_core/src/coffee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ impl PluginManager for CoffeeManager {
Err(err)
}

async fn nurse(&mut self) -> Result<(), CoffeeError> {
async fn nurse(&mut self) -> Result<CoffeeNurse, CoffeeError> {
self.recovery_strategies.scan().await
}
}
Expand Down
20 changes: 12 additions & 8 deletions coffee_core/src/nurse/chain.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Nurse Chain of Responsibility rust implementation
//!
//! If you do not know what Chain Of Responsibility patter
//! If you do not know what Chain Of Responsibility pattern
//! is, here is a small description:
//!
//! > Chain of Responsibility is behavioral design pattern
Expand All @@ -14,7 +14,7 @@
//! > a standard handler interface.
//!
//! In our case we do not need to handle a request, but we should
//! handler through the various recovery strategy to see what can
//! handler through the various recovery strategies to see what can
//! be applied.
//!
//! So in our case the handler is a specific recovery strategy
Expand All @@ -31,12 +31,14 @@ use std::sync::Arc;
use async_trait::async_trait;

use coffee_lib::errors::CoffeeError;
use coffee_lib::types::response::{CoffeeNurse, NurseStatus};

use super::strategy::GitRepositoryLocallyAbsentStrategy;
use super::strategy::RecoveryStrategy;

#[async_trait]
pub trait Handler: Send + Sync {
async fn can_be_apply(
async fn can_be_applied(
self: Arc<Self>,
) -> Result<Option<Arc<dyn RecoveryStrategy>>, CoffeeError>;
}
Expand All @@ -48,16 +50,18 @@ pub struct RecoveryChainOfResponsibility {
impl RecoveryChainOfResponsibility {
pub async fn new() -> Result<Self, CoffeeError> {
Ok(Self {
handlers: Vec::new(),
handlers: vec![Arc::new(GitRepositoryLocallyAbsentStrategy)],
})
}

pub async fn scan(&self) -> Result<(), CoffeeError> {
pub async fn scan(&self) -> Result<CoffeeNurse, CoffeeError> {
for handler in self.handlers.iter() {
if let Some(strategy) = handler.clone().can_be_apply().await? {
strategy.patch().await?;
if let Some(strategy) = handler.clone().can_be_applied().await? {
return strategy.patch().await;
}
}
Ok(())
Ok(CoffeeNurse {
status: NurseStatus::Sane,
})
}
}
41 changes: 34 additions & 7 deletions coffee_core/src/nurse/strategy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,53 @@ use std::sync::Arc;
use async_trait::async_trait;

use coffee_lib::errors::CoffeeError;
use coffee_lib::types::response::{CoffeeNurse, NurseStatus};

use crate::nurse::chain::Handler;

#[async_trait]
pub trait RecoveryStrategy: Send + Sync {
async fn patch(&self) -> Result<(), CoffeeError>;
async fn patch(&self) -> Result<CoffeeNurse, CoffeeError>;
}

pub struct GitRepositoryMissedStrategy;
/// Strategy for handling the situation when a Git repository exists in coffee configuration
/// but is absent from the local storage.
///
/// This strategy is invoked when a Git repository is documented in the coffee configuration but
/// is not found in the local storage directory (./coffee/repositories).
/// The absence of the repository locally may be due to reasons such as accidental deletion or
/// a change in the storage location.
pub struct GitRepositoryLocallyAbsentStrategy;

#[async_trait]
impl RecoveryStrategy for GitRepositoryMissedStrategy {
async fn patch(&self) -> Result<(), CoffeeError> {
unimplemented!()
impl RecoveryStrategy for GitRepositoryLocallyAbsentStrategy {
/// Attempts to address the absence of a Git repository from local storage.
///
/// This method is responsible for managing the scenario where a Git repository is listed
/// in the coffee configuration but is not present in the `.coffee/repositories` folder.
///
/// It takes the following actions:
///
/// 1. Attempts to clone the repository using the Git HEAD reference stored in the configuration.
/// This is done in an effort to retrieve the missing repository from its source.
///
/// 2. If the cloning process fails, it will remove the repository entry from the coffee configuration.
async fn patch(&self) -> Result<CoffeeNurse, CoffeeError> {
Ok(CoffeeNurse {
status: NurseStatus::RepositoryLocallyAbsent,
})
}
}

#[async_trait]
impl Handler for GitRepositoryMissedStrategy {
async fn can_be_apply(
impl Handler for GitRepositoryLocallyAbsentStrategy {
/// Determines if [`GitRepositoryLocallyAbsentStrategy`] can be applied.
///
/// This function iterates over the Git repositories listed in the coffee configuration and
/// checks if each one exists in the `.coffee/repositories` folder. If any repository is found
/// to be missing from local storage, it indicates that the strategy to handle
/// this situation should be applied.
async fn can_be_applied(
self: Arc<Self>,
) -> Result<Option<std::sync::Arc<dyn RecoveryStrategy>>, CoffeeError> {
Ok(Some(self))
Expand Down
2 changes: 1 addition & 1 deletion coffee_lib/src/plugin_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,5 @@ pub trait PluginManager {
async fn search(&mut self, plugin: &str) -> Result<CoffeeSearch, CoffeeError>;

/// clean up storage information about the remote repositories of the plugin manager.
async fn nurse(&mut self) -> Result<(), CoffeeError>;
async fn nurse(&mut self) -> Result<CoffeeNurse, CoffeeError>;
}
16 changes: 16 additions & 0 deletions coffee_lib/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,20 @@ pub mod response {
pub repository_url: String,
pub plugin: Plugin,
}

/// This struct is used to represent the status of nurse,
/// either sane or not.
/// If not sane, return the reason of the problem.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum NurseStatus {
Sane,
RepositoryLocallyAbsent,
RepositoryLocallyCorrupt,
RepositoryMissingInConfiguration,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CoffeeNurse {
pub status: NurseStatus,
}
}

0 comments on commit 93eb17d

Please sign in to comment.