bitpill 0.3.5

A personal medication management TUI application built in Rust.
Documentation
use std::sync::Arc;

use crate::application::{
    dtos::{
        requests::{SettingsOperation, SettingsRequest},
        responses::SettingsResponse,
    },
    errors::ApplicationError,
    ports::{
        inbound::settings_port::SettingsPort,
        outbound::settings_repository_port::SettingsRepositoryPort,
    },
};

pub struct SettingsService {
    repository: Arc<dyn SettingsRepositoryPort>,
}

impl SettingsService {
    pub fn new(repository: Arc<dyn SettingsRepositoryPort>) -> Self {
        Self { repository }
    }
}

impl SettingsPort for SettingsService {
    fn execute(&self, req: SettingsRequest) -> Result<SettingsResponse, ApplicationError> {
        match req.op {
            SettingsOperation::Get => {
                let v = self.repository.load()?;
                Ok(SettingsResponse { settings: v })
            }
            SettingsOperation::Update { settings } => {
                self.repository.save(&settings)?;
                Ok(SettingsResponse { settings })
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;

    struct InMemorySettingsRepo {
        value: std::sync::Mutex<serde_json::Value>,
    }

    impl InMemorySettingsRepo {
        fn new(v: serde_json::Value) -> Self {
            Self {
                value: std::sync::Mutex::new(v),
            }
        }
    }

    impl crate::application::ports::outbound::settings_repository_port::SettingsRepositoryPort
        for InMemorySettingsRepo
    {
        fn load(&self) -> Result<serde_json::Value, crate::application::errors::StorageError> {
            Ok(self.value.lock().unwrap().clone())
        }

        fn save(
            &self,
            settings: &serde_json::Value,
        ) -> Result<(), crate::application::errors::StorageError> {
            *self.value.lock().unwrap() = settings.clone();
            Ok(())
        }
    }

    fn make_service(repo: std::sync::Arc<InMemorySettingsRepo>) -> SettingsService {
        SettingsService::new(repo)
    }

    #[test]
    fn execute_get_returns_saved_settings() {
        let repo = std::sync::Arc::new(InMemorySettingsRepo::new(json!({"k": "v"})));
        let service = make_service(repo);
        let req = SettingsRequest {
            op: SettingsOperation::Get,
        };

        let res = service.execute(req).unwrap();
        assert_eq!(res.settings, json!({"k":"v"}));
    }

    #[test]
    fn execute_update_saves_and_returns_settings() {
        let repo = std::sync::Arc::new(InMemorySettingsRepo::new(json!({})));
        let service = make_service(repo.clone());
        let new_settings = json!({"a":1});
        let req = SettingsRequest {
            op: SettingsOperation::Update {
                settings: new_settings.clone(),
            },
        };

        let res = service.execute(req).unwrap();
        assert_eq!(res.settings, new_settings);
        assert_eq!(*repo.value.lock().unwrap(), new_settings);
    }
}