lab_resource_manager/application/usecases/
update_resource_usage.rs

1use crate::application::error::ApplicationError;
2use crate::domain::aggregates::resource_usage::value_objects::{TimePeriod, UsageId};
3use crate::domain::common::EmailAddress;
4use crate::domain::ports::repositories::{RepositoryError, ResourceUsageRepository};
5use crate::domain::services::{
6    AuthorizationPolicy, ResourceConflictChecker, ResourceUsageAuthorizationPolicy,
7};
8use std::sync::Arc;
9
10/// リソース使用予定を更新するユースケース
11pub struct UpdateResourceUsageUseCase<R: ResourceUsageRepository> {
12    repository: Arc<R>,
13    authorization_policy: ResourceUsageAuthorizationPolicy,
14    conflict_checker: ResourceConflictChecker,
15}
16
17impl<R: ResourceUsageRepository> UpdateResourceUsageUseCase<R> {
18    /// 新しいUpdateResourceUsageUseCaseインスタンスを作成
19    ///
20    /// # Arguments
21    /// * `repository` - ResourceUsageリポジトリ
22    pub fn new(repository: Arc<R>) -> Self {
23        let authorization_policy = ResourceUsageAuthorizationPolicy::new();
24        let conflict_checker = ResourceConflictChecker::new();
25        Self {
26            repository,
27            authorization_policy,
28            conflict_checker,
29        }
30    }
31
32    /// リソース使用予定を更新
33    ///
34    /// # Arguments
35    /// * `id` - 使用予定ID
36    /// * `owner_email` - 所有者のメールアドレス(権限チェック用)
37    /// * `new_time_period` - 新しい使用期間(Noneの場合は変更なし)
38    /// * `new_notes` - 新しい備考(Noneの場合は変更なし)
39    ///
40    /// # Returns
41    /// 更新成功
42    ///
43    /// # Errors
44    /// - 指定されたIDの予約が見つからない場合
45    /// - 所有者が一致しない場合
46    /// - 新しい時間枠が競合する場合
47    /// - リポジトリエラー
48    pub async fn execute(
49        &self,
50        id: &UsageId,
51        owner_email: &EmailAddress,
52        new_time_period: Option<TimePeriod>,
53        new_notes: Option<String>,
54    ) -> Result<(), ApplicationError> {
55        // 既存の予約を取得
56        let mut usage = self
57            .repository
58            .find_by_id(id)
59            .await?
60            .ok_or(ApplicationError::Repository(RepositoryError::NotFound))?;
61
62        // 認可チェック
63        self.authorization_policy
64            .authorize_update(owner_email, &usage)
65            .map_err(|e| ApplicationError::Unauthorized(e.to_string()))?;
66
67        // 時間枠の更新と競合チェック
68        if let Some(new_period) = new_time_period {
69            // 競合チェック(自分自身を除外)
70            self.conflict_checker
71                .check_conflicts(
72                    self.repository.as_ref(),
73                    &new_period,
74                    usage.resources(),
75                    Some(usage.id()),
76                )
77                .await
78                .map_err(|e| match e {
79                    crate::domain::services::resource_usage::errors::ConflictCheckError::Conflict(
80                        conflict_err,
81                    ) => ApplicationError::ResourceConflict {
82                        resource_description: conflict_err.resource_description.clone(),
83                        conflicting_usage_id: conflict_err.conflicting_usage_id.as_str().to_string(),
84                    },
85                    crate::domain::services::resource_usage::errors::ConflictCheckError::Repository(
86                        repo_err,
87                    ) => ApplicationError::Repository(repo_err),
88                })?;
89
90            usage.update_time_period(new_period);
91        }
92
93        // 備考の更新
94        if let Some(notes) = new_notes {
95            usage.update_notes(notes);
96        }
97
98        // 更新
99        self.repository.save(&usage).await?;
100
101        Ok(())
102    }
103}