backend/service/
seed.rs

1//! Service layer for seeds.
2
3use chrono::Utc;
4use uuid::Uuid;
5
6use crate::{
7    config::data::SharedPool,
8    error::ServiceError,
9    model::{
10        dto::{ArchiveSeedDto, NewSeedDto, Page, PageParameters, SeedDto, SeedSearchParameters},
11        entity::{NewSeed, Seed},
12    },
13};
14
15/// Search seeds from the database.
16/// Seeds are returned in ascending order of their `use_by` dates.
17/// If that is not available, the harvest year is used instead.
18///
19/// By default, archived seeds will not be returned.
20/// This behaviour can be changed using `search_parameters`.
21///
22/// # Errors
23/// If the connection to the database could not be established.
24pub async fn find(
25    search_parameters: SeedSearchParameters,
26    page_parameters: PageParameters,
27    user_id: Uuid,
28    pool: &SharedPool,
29) -> Result<Page<SeedDto>, ServiceError> {
30    let mut conn = pool.get().await?;
31    let result = Seed::find(search_parameters, user_id, page_parameters, &mut conn).await?;
32    Ok(result)
33}
34
35/// Find the seed by id from the database.
36///
37/// # Errors
38/// If the connection to the database could not be established.
39pub async fn find_by_id(
40    id: i64,
41    user_id: Uuid,
42    pool: &SharedPool,
43) -> Result<SeedDto, ServiceError> {
44    let mut conn = pool.get().await?;
45    let result = Seed::find_by_id(id, user_id, &mut conn).await?;
46    Ok(result)
47}
48
49/// Create a new seed in the database.
50///
51/// # Errors
52/// If the connection to the database could not be established.
53pub async fn create(
54    new_seed: NewSeedDto,
55    user_id: Uuid,
56    pool: &SharedPool,
57) -> Result<SeedDto, ServiceError> {
58    let mut conn = pool.get().await?;
59
60    let seed_trimmed_name = NewSeedDto {
61        name: new_seed.name.trim().to_owned(),
62        ..new_seed
63    };
64
65    let new_seed_entity = NewSeed::try_from((seed_trimmed_name, user_id))?;
66
67    let result = Seed::create(new_seed_entity, &mut conn).await?;
68    Ok(result)
69}
70
71/// Edits a seed in the database.
72///
73/// # Errors
74/// If the connection to the database could not be established.
75pub async fn edit(
76    id: i64,
77    user_id: Uuid,
78    new_seed: NewSeedDto,
79    pool: &SharedPool,
80) -> Result<SeedDto, ServiceError> {
81    let mut conn = pool.get().await?;
82    let new_seed_entity = NewSeed::try_from((new_seed, user_id))?;
83    let result = Seed::edit(id, new_seed_entity, &mut conn).await?;
84    Ok(result)
85}
86
87/// Delete the seed from the database.
88///
89/// # Errors
90/// If the connection to the database could not be established.
91pub async fn delete_by_id(id: i64, user_id: Uuid, pool: &SharedPool) -> Result<(), ServiceError> {
92    let mut conn = pool.get().await?;
93    let _ = Seed::delete_by_id(id, user_id, &mut conn).await?;
94    Ok(())
95}
96
97/// Archive or unarchive a seed in the database.
98///
99/// # Errors
100/// If the connection to the database could not be established.
101pub async fn archive(
102    id: i64,
103    user_id: Uuid,
104    archive_seed: ArchiveSeedDto,
105    pool: &SharedPool,
106) -> Result<SeedDto, ServiceError> {
107    // Retrieve the seed before getting the db connection to avoid deadlocks when
108    // fetching two database connections at the same time.
109    let current_seed = find_by_id(id, user_id, pool).await?;
110    let mut conn = pool.get().await?;
111
112    let current_naive_date_time = archive_seed.archived.then(|| Utc::now().naive_utc());
113
114    // Don't archive a seed twice
115    if archive_seed.archived && current_seed.archived_at.is_some() {
116        return Ok(current_seed);
117    }
118
119    let result = Seed::archive(id, current_naive_date_time, user_id, &mut conn).await?;
120    Ok(result)
121}