//! Actor service layer providing business logic for actor management operations. //! //! Implements the service layer of the actor management system, handling business logic, //! validation, and orchestration. //! //! For full documentation, architecture, and examples, see the [crate README](../README.md). use forge_models::Actor; use forge_repositories::ActorRepository; /// Service layer implementation for actor business logic and operations. /// /// Orchestrates actor management operations, handling business logic, validation, /// and data transformation. See [crate README](../README.md) for details. /// /// # Thread Safety /// Thread-safe when used with a thread-safe repository. pub struct ActorService { /// The repository instance used for all data persistence operations. /// /// This repository handles the actual storage and retrieval of actor data, /// abstracting away the specific database implementation details. repository: R, } impl ActorService { /// Creates a new actor service with the provided repository. /// /// The repository must be initialized and ready for use. pub fn new(repository: R) -> Self { Self { repository } } /// Creates a new actor with the provided ID and JSON data. /// /// Handles validation, duplicate checking, and persistence. /// See [crate README](../README.md) for JSON format and business rules. pub fn create_actor(&self, actor_id: String, json_data: String) -> Result { // Create base actor with the provided UID let new_actor = Actor::new(actor_id.clone()).map_err(|e| e.to_string())?; // Check if actor already exists to prevent duplicates if self.repository.exists(&actor_id)? { return Err(format!("Actor with UID '{}' already exists", actor_id)); } // Parse and validate JSON input let json_obj: serde_json::Value = serde_json::from_str(&json_data).map_err(|e| format!("Invalid Actor JSON: {}", e))?; // Ensure JSON is an object (not array, string, etc.) if let serde_json::Value::Object(mut map) = json_obj { // Remove UID field to prevent conflicts (UID is immutable) map.remove("uid"); let update_data = serde_json::Value::Object(map); // Apply JSON data to the base actor let mut final_actor = new_actor; final_actor.update_from_json(update_data)?; // Store the actor in the repository self.repository.create(&final_actor)?; Ok(final_actor) } else { Err("JSON data must be an object".to_string()) } } /// Retrieves an actor by their unique identifier with automatic fallback creation. /// /// Implements a "get-or-create" pattern: if the actor doesn't exist, a new one /// with default values is returned (but not persisted). pub fn get_actor(&self, actor_id: String) -> Result { // Attempt to retrieve actor from repository match self.repository.get_by_id(&actor_id)? { // Actor found - return it Some(actor) => Ok(actor), // Actor not found - create fallback actor with default values None => Actor::new(actor_id).map_err(|e| e.to_string()), } } /// Updates an existing actor with new data from JSON. /// /// Handles partial updates, validation, and persistence. /// See [crate README](../README.md) for JSON format and concurrency details. pub fn update_actor(&self, actor_id: String, json_update: String) -> Result { // Retrieve existing actor from repository let mut actor = match self.repository.get_by_id(&actor_id)? { Some(actor) => actor, None => return Err(format!("Actor with UID '{}' not found", actor_id)), }; // Parse and validate JSON update data let update_data: serde_json::Value = serde_json::from_str(&json_update).map_err(|e| format!("Invalid JSON: {}", e))?; // Ensure update data is a JSON object if !update_data.is_object() { return Err("Update data must be a JSON object".to_string()); } // Apply updates to the actor (this validates the changes) actor.update_from_json(update_data)?; // Persist the updated actor to repository self.repository.update(&actor)?; Ok(actor) } /// Permanently deletes an actor from the system. /// /// Irreversible operation. Delegates to repository. pub fn delete_actor(&self, actor_id: String) -> Result<(), String> { // Delegate deletion to repository layer // Future enhancements could add business logic here: // - Authorization checks // - Audit logging // - Cascade deletion // - Soft deletion self.repository.delete(&actor_id) } /// Checks if an actor exists in the system. /// /// Lightweight check without data retrieval. pub fn actor_exists(&self, actor_id: String) -> Result { // Delegate existence check to repository layer // This is a lightweight operation that doesn't retrieve data self.repository.exists(&actor_id) } }