koprogo_api/application/use_cases/
skill_use_cases.rs1use crate::application::dto::{
2 CategoryCount, CreateSkillDto, ExpertiseCount, SkillResponseDto, SkillStatisticsDto,
3 SkillSummaryDto, UpdateSkillDto,
4};
5use crate::application::ports::{OwnerRepository, SkillRepository};
6use crate::domain::entities::{ExpertiseLevel, Skill, SkillCategory};
7use std::sync::Arc;
8use uuid::Uuid;
9
10pub struct SkillUseCases {
11 skill_repo: Arc<dyn SkillRepository>,
12 owner_repo: Arc<dyn OwnerRepository>,
13}
14
15impl SkillUseCases {
16 pub fn new(skill_repo: Arc<dyn SkillRepository>, owner_repo: Arc<dyn OwnerRepository>) -> Self {
17 Self {
18 skill_repo,
19 owner_repo,
20 }
21 }
22
23 pub async fn create_skill(
28 &self,
29 owner_id: Uuid,
30 dto: CreateSkillDto,
31 ) -> Result<SkillResponseDto, String> {
32 let owner = self
34 .owner_repo
35 .find_by_id(owner_id)
36 .await?
37 .ok_or("Owner not found".to_string())?;
38
39 let skill = Skill::new(
41 owner_id,
42 dto.building_id,
43 dto.skill_category,
44 dto.skill_name,
45 dto.expertise_level,
46 dto.description,
47 dto.is_available_for_help,
48 dto.hourly_rate_credits,
49 dto.years_of_experience,
50 dto.certifications,
51 )?;
52
53 let created = self.skill_repo.create(&skill).await?;
55
56 let owner_name = format!("{} {}", owner.first_name, owner.last_name);
58 Ok(SkillResponseDto::from_skill(created, owner_name))
59 }
60
61 pub async fn get_skill(&self, skill_id: Uuid) -> Result<SkillResponseDto, String> {
63 let skill = self
64 .skill_repo
65 .find_by_id(skill_id)
66 .await?
67 .ok_or("Skill not found".to_string())?;
68
69 let owner = self
71 .owner_repo
72 .find_by_id(skill.owner_id)
73 .await?
74 .ok_or("Owner not found".to_string())?;
75
76 let owner_name = format!("{} {}", owner.first_name, owner.last_name);
77 Ok(SkillResponseDto::from_skill(skill, owner_name))
78 }
79
80 pub async fn list_building_skills(
85 &self,
86 building_id: Uuid,
87 ) -> Result<Vec<SkillSummaryDto>, String> {
88 let skills = self.skill_repo.find_by_building(building_id).await?;
89 self.enrich_skills_summary(skills).await
90 }
91
92 pub async fn list_available_skills(
97 &self,
98 building_id: Uuid,
99 ) -> Result<Vec<SkillSummaryDto>, String> {
100 let skills = self
101 .skill_repo
102 .find_available_by_building(building_id)
103 .await?;
104 self.enrich_skills_summary(skills).await
105 }
106
107 pub async fn list_owner_skills(&self, owner_id: Uuid) -> Result<Vec<SkillSummaryDto>, String> {
109 let skills = self.skill_repo.find_by_owner(owner_id).await?;
110 self.enrich_skills_summary(skills).await
111 }
112
113 pub async fn list_skills_by_category(
115 &self,
116 building_id: Uuid,
117 category: SkillCategory,
118 ) -> Result<Vec<SkillSummaryDto>, String> {
119 let skills = self
120 .skill_repo
121 .find_by_category(building_id, category)
122 .await?;
123 self.enrich_skills_summary(skills).await
124 }
125
126 pub async fn list_skills_by_expertise(
128 &self,
129 building_id: Uuid,
130 level: ExpertiseLevel,
131 ) -> Result<Vec<SkillSummaryDto>, String> {
132 let skills = self
133 .skill_repo
134 .find_by_expertise(building_id, level)
135 .await?;
136 self.enrich_skills_summary(skills).await
137 }
138
139 pub async fn list_free_skills(
141 &self,
142 building_id: Uuid,
143 ) -> Result<Vec<SkillSummaryDto>, String> {
144 let skills = self.skill_repo.find_free_by_building(building_id).await?;
145 self.enrich_skills_summary(skills).await
146 }
147
148 pub async fn list_professional_skills(
150 &self,
151 building_id: Uuid,
152 ) -> Result<Vec<SkillSummaryDto>, String> {
153 let skills = self
154 .skill_repo
155 .find_professional_by_building(building_id)
156 .await?;
157 self.enrich_skills_summary(skills).await
158 }
159
160 pub async fn update_skill(
165 &self,
166 skill_id: Uuid,
167 actor_id: Uuid,
168 dto: UpdateSkillDto,
169 ) -> Result<SkillResponseDto, String> {
170 let mut skill = self
171 .skill_repo
172 .find_by_id(skill_id)
173 .await?
174 .ok_or("Skill not found".to_string())?;
175
176 if skill.owner_id != actor_id {
178 return Err("Unauthorized: only owner can update skill".to_string());
179 }
180
181 skill.update(
183 dto.skill_name,
184 dto.expertise_level,
185 dto.description,
186 dto.is_available_for_help,
187 dto.hourly_rate_credits,
188 dto.years_of_experience,
189 dto.certifications,
190 )?;
191
192 let updated = self.skill_repo.update(&skill).await?;
194
195 self.get_skill(updated.id).await
197 }
198
199 pub async fn mark_skill_available(
204 &self,
205 skill_id: Uuid,
206 actor_id: Uuid,
207 ) -> Result<SkillResponseDto, String> {
208 let mut skill = self
209 .skill_repo
210 .find_by_id(skill_id)
211 .await?
212 .ok_or("Skill not found".to_string())?;
213
214 if skill.owner_id != actor_id {
216 return Err("Unauthorized: only owner can mark skill as available".to_string());
217 }
218
219 skill.mark_available();
221
222 let updated = self.skill_repo.update(&skill).await?;
224
225 self.get_skill(updated.id).await
227 }
228
229 pub async fn mark_skill_unavailable(
234 &self,
235 skill_id: Uuid,
236 actor_id: Uuid,
237 ) -> Result<SkillResponseDto, String> {
238 let mut skill = self
239 .skill_repo
240 .find_by_id(skill_id)
241 .await?
242 .ok_or("Skill not found".to_string())?;
243
244 if skill.owner_id != actor_id {
246 return Err("Unauthorized: only owner can mark skill as unavailable".to_string());
247 }
248
249 skill.mark_unavailable();
251
252 let updated = self.skill_repo.update(&skill).await?;
254
255 self.get_skill(updated.id).await
257 }
258
259 pub async fn delete_skill(&self, skill_id: Uuid, actor_id: Uuid) -> Result<(), String> {
264 let skill = self
265 .skill_repo
266 .find_by_id(skill_id)
267 .await?
268 .ok_or("Skill not found".to_string())?;
269
270 if skill.owner_id != actor_id {
272 return Err("Unauthorized: only owner can delete skill".to_string());
273 }
274
275 self.skill_repo.delete(skill_id).await?;
277
278 Ok(())
279 }
280
281 pub async fn get_skill_statistics(
283 &self,
284 building_id: Uuid,
285 ) -> Result<SkillStatisticsDto, String> {
286 let total_skills = self.skill_repo.count_by_building(building_id).await?;
287 let available_skills = self
288 .skill_repo
289 .count_available_by_building(building_id)
290 .await?;
291
292 let skills = self.skill_repo.find_by_building(building_id).await?;
294 let free_skills = skills.iter().filter(|s| s.is_free()).count() as i64;
295 let paid_skills = total_skills - free_skills;
296 let professional_skills = skills.iter().filter(|s| s.is_professional()).count() as i64;
297
298 let mut skills_by_category = Vec::new();
300 for category in [
301 SkillCategory::HomeRepair,
302 SkillCategory::Languages,
303 SkillCategory::Technology,
304 SkillCategory::Education,
305 SkillCategory::Arts,
306 SkillCategory::Sports,
307 SkillCategory::Cooking,
308 SkillCategory::Gardening,
309 SkillCategory::Health,
310 SkillCategory::Legal,
311 SkillCategory::Financial,
312 SkillCategory::PetCare,
313 SkillCategory::Other,
314 ] {
315 let count = self
316 .skill_repo
317 .count_by_category(building_id, category.clone())
318 .await?;
319 if count > 0 {
320 skills_by_category.push(CategoryCount { category, count });
321 }
322 }
323
324 let mut skills_by_expertise = Vec::new();
326 for level in [
327 ExpertiseLevel::Beginner,
328 ExpertiseLevel::Intermediate,
329 ExpertiseLevel::Advanced,
330 ExpertiseLevel::Expert,
331 ] {
332 let count = self
333 .skill_repo
334 .count_by_expertise(building_id, level.clone())
335 .await?;
336 if count > 0 {
337 skills_by_expertise.push(ExpertiseCount { level, count });
338 }
339 }
340
341 Ok(SkillStatisticsDto {
342 total_skills,
343 available_skills,
344 free_skills,
345 paid_skills,
346 professional_skills,
347 skills_by_category,
348 skills_by_expertise,
349 })
350 }
351
352 async fn enrich_skills_summary(
354 &self,
355 skills: Vec<Skill>,
356 ) -> Result<Vec<SkillSummaryDto>, String> {
357 let mut enriched = Vec::new();
358
359 for skill in skills {
360 let owner = self.owner_repo.find_by_id(skill.owner_id).await?;
362 let owner_name = if let Some(owner) = owner {
363 format!("{} {}", owner.first_name, owner.last_name)
364 } else {
365 "Unknown Owner".to_string()
366 };
367
368 enriched.push(SkillSummaryDto::from_skill(skill, owner_name));
369 }
370
371 Ok(enriched)
372 }
373}