1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//! Service layer for areas

use actix_http::StatusCode;
use chrono::Days;
use uuid::Uuid;

use crate::{
    config::data::SharedPool,
    error::ServiceError,
    model::{
        dto::{
            areas::{AreaDto, AreaKind, AreaSearchParameters, NewAreaDto, UpdateAreaDto},
            areas_impl::{
                from_new_area_dto_to_hydrology, from_new_area_dto_to_shading,
                from_new_area_dto_to_soil_texture, from_update_area_dto_to_update_hydrology,
                from_update_area_dto_to_update_shading,
                from_update_area_dto_to_update_soil_texture,
            },
            core::TimelinePage,
        },
        entity::{
            areas::{Hydrology, Shading, SoilTexture},
            areas_impl::{
                create_hydrologies, create_shadings, create_soil_textures, delete_areas_by_ids,
                find_area, update_hydrologies, update_shadings, update_soil_textures,
                FindAreaParameters,
            },
        },
    },
};

/// Time offset in days for loading areas in the timeline.
pub const TIME_LINE_LOADING_OFFSET_DAYS: u64 = 356;

/// Search areas from the database.
///
/// # Errors
/// If the connection to the database could not be established.
pub async fn find(
    search_parameters: AreaSearchParameters,
    pool: &SharedPool,
) -> Result<TimelinePage<AreaDto>, ServiceError> {
    let mut conn = pool.get().await?;

    let from = search_parameters
        .relative_to_date
        .checked_sub_days(Days::new(TIME_LINE_LOADING_OFFSET_DAYS))
        .ok_or_else(|| {
            ServiceError::new(
                StatusCode::BAD_REQUEST,
                "Could not add days to relative_to_date",
            )
        })?;

    let to = search_parameters
        .relative_to_date
        .checked_add_days(Days::new(TIME_LINE_LOADING_OFFSET_DAYS))
        .ok_or_else(|| {
            ServiceError::new(
                StatusCode::BAD_REQUEST,
                "Could not add days to relative_to_date",
            )
        })?;

    let search_parameters = FindAreaParameters {
        area_kind: search_parameters.kind,
        layer_id: search_parameters.layer_id,
        from,
        to,
    };
    let result: Vec<_> = find_area(search_parameters, &mut conn).await?;

    Ok(TimelinePage {
        results: result,
        from,
        to,
    })
}

/// Create a new area in the database.
///
/// # Errors
/// If the connection to the database could not be established.
pub async fn create(
    area_kind: AreaKind,
    dtos: Vec<NewAreaDto>,
    pool: &SharedPool,
) -> Result<Vec<AreaDto>, ServiceError> {
    let mut conn = pool.get().await?;

    let result = match area_kind {
        AreaKind::Shade => {
            let new_shadings: Vec<Shading> = from_new_area_dto_to_shading(dtos)?;
            create_shadings(new_shadings, &mut conn).await?
        }
        AreaKind::Hydrology => {
            let new_hydrologies: Vec<Hydrology> = from_new_area_dto_to_hydrology(dtos)?;
            create_hydrologies(new_hydrologies, &mut conn).await?
        }
        AreaKind::SoilTexture => {
            let new_soil_textures: Vec<SoilTexture> = from_new_area_dto_to_soil_texture(dtos)?;
            create_soil_textures(new_soil_textures, &mut conn).await?
        }
    };
    Ok(result)
}

/// Update the areas in the database.
///
/// # Errors
/// If the connection to the database could not be established.
pub async fn update(
    area_kind: AreaKind,
    dto: UpdateAreaDto,
    pool: &SharedPool,
) -> Result<Vec<AreaDto>, ServiceError> {
    let mut conn = pool.get().await?;

    let result = match area_kind {
        AreaKind::Shade => {
            let updates = from_update_area_dto_to_update_shading(dto)?;
            update_shadings(updates, &mut conn).await?
        }
        AreaKind::Hydrology => {
            let updates = from_update_area_dto_to_update_hydrology(dto)?;
            update_hydrologies(updates, &mut conn).await?
        }
        AreaKind::SoilTexture => {
            let updates = from_update_area_dto_to_update_soil_texture(dto)?;
            update_soil_textures(updates, &mut conn).await?
        }
    };

    Ok(result)
}

/// Delete the areas from the database.
///
/// # Errors
/// If the connection to the database could not be established.
pub async fn delete_by_ids(
    area_kind: AreaKind,
    dtos: Vec<Uuid>,
    pool: &SharedPool,
) -> Result<(), ServiceError> {
    let mut conn = pool.get().await?;
    delete_areas_by_ids(area_kind, dtos, &mut conn).await?;
    Ok(())
}