backend/model/entity/
areas_impl.rs

1//! Contains the implementation of `Shading`, `Hydrology` and `SoilTexture`
2
3use chrono::NaiveDate;
4use diesel::{
5    debug_query, pg::Pg, BoolExpressionMethods, ExpressionMethods, QueryDsl, QueryResult,
6};
7use diesel_async::{AsyncConnection, AsyncPgConnection, RunQueryDsl};
8use log::debug;
9use uuid::Uuid;
10
11use crate::{
12    model::{
13        dto::areas::{AreaDto, AreaKind},
14        entity::areas::{
15            Hydrology, Shading, SoilTexture, UpdateHydrology, UpdateShading, UpdateSoilTexture,
16        },
17    },
18    schema::{hydrologies, shadings, soil_textures},
19};
20
21/// Arguments for the database layer find shadings function.
22pub struct FindAreaParameters {
23    /// Type of area
24    pub area_kind: AreaKind,
25    /// The id of the layer to find shadings for.
26    pub layer_id: Option<Uuid>,
27    /// First date in the time frame shadings are searched for.
28    pub from: NaiveDate,
29    /// Last date in the time frame shadings are searched for.
30    pub to: NaiveDate,
31}
32
33/// Get all areas from the appropriate table.
34///
35/// # Errors
36/// * Unknown, diesel doesn't say why it might error.
37pub async fn find_area(
38    search_parameters: FindAreaParameters,
39    conn: &mut AsyncPgConnection,
40) -> QueryResult<Vec<AreaDto>> {
41    match search_parameters.area_kind {
42        AreaKind::Shade => {
43            let mut query = shadings::table.select(shadings::all_columns).into_boxed();
44
45            if let Some(id) = search_parameters.layer_id {
46                query = query.filter(shadings::layer_id.eq(id));
47            }
48
49            let added_before_date = shadings::add_date
50                .is_null()
51                .or(shadings::add_date.lt(search_parameters.to));
52            let removed_after_date = shadings::remove_date
53                .is_null()
54                .or(shadings::remove_date.gt(search_parameters.from));
55            query = query.filter(added_before_date.and(removed_after_date));
56
57            debug!("{}", debug_query::<Pg, _>(&query));
58
59            Ok(query
60                .load::<Shading>(conn)
61                .await?
62                .into_iter()
63                .map(Into::into)
64                .collect())
65        }
66        AreaKind::Hydrology => {
67            let mut query = hydrologies::table
68                .select(hydrologies::all_columns)
69                .into_boxed();
70
71            if let Some(id) = search_parameters.layer_id {
72                query = query.filter(hydrologies::layer_id.eq(id));
73            }
74
75            let added_before_date = hydrologies::add_date
76                .is_null()
77                .or(hydrologies::add_date.lt(search_parameters.to));
78            let removed_after_date = hydrologies::remove_date
79                .is_null()
80                .or(hydrologies::remove_date.gt(search_parameters.from));
81            query = query.filter(added_before_date.and(removed_after_date));
82
83            debug!("{}", debug_query::<Pg, _>(&query));
84
85            Ok(query
86                .load::<Hydrology>(conn)
87                .await?
88                .into_iter()
89                .map(Into::into)
90                .collect())
91        }
92        AreaKind::SoilTexture => {
93            let mut query = soil_textures::table
94                .select(soil_textures::all_columns)
95                .into_boxed();
96
97            if let Some(id) = search_parameters.layer_id {
98                query = query.filter(soil_textures::layer_id.eq(id));
99            }
100
101            let added_before_date = soil_textures::add_date
102                .is_null()
103                .or(soil_textures::add_date.lt(search_parameters.to));
104            let removed_after_date = soil_textures::remove_date
105                .is_null()
106                .or(soil_textures::remove_date.gt(search_parameters.from));
107            query = query.filter(added_before_date.and(removed_after_date));
108
109            debug!("{}", debug_query::<Pg, _>(&query));
110
111            Ok(query
112                .load::<SoilTexture>(conn)
113                .await?
114                .into_iter()
115                .map(Into::into)
116                .collect())
117        }
118    }
119}
120
121/// Save shadings in the database.
122///
123/// # Errors
124/// * Unknown, diesel doesn't say why it might error.
125pub async fn create_shadings(
126    new_shadings: Vec<Shading>,
127    conn: &mut AsyncPgConnection,
128) -> QueryResult<Vec<AreaDto>> {
129    let query = diesel::insert_into(shadings::table).values(&new_shadings);
130    debug!("{}", debug_query::<Pg, _>(&query));
131    let shadings = query
132        .get_results::<Shading>(conn)
133        .await?
134        .into_iter()
135        .map(Into::into)
136        .collect::<Vec<AreaDto>>();
137    Ok(shadings)
138}
139
140/// Save hydrologies in the database.
141///
142/// # Errors
143/// * Unknown, diesel doesn't say why it might error.
144pub async fn create_hydrologies(
145    new_hydrologies: Vec<Hydrology>,
146    conn: &mut AsyncPgConnection,
147) -> QueryResult<Vec<AreaDto>> {
148    let query = diesel::insert_into(hydrologies::table).values(&new_hydrologies);
149    debug!("{}", debug_query::<Pg, _>(&query));
150    let hydrologies = query
151        .get_results::<Hydrology>(conn)
152        .await?
153        .into_iter()
154        .map(Into::into)
155        .collect::<Vec<AreaDto>>();
156    Ok(hydrologies)
157}
158
159/// Save soil textures in the database.
160///
161/// # Errors
162/// * Unknown, diesel doesn't say why it might error.
163pub async fn create_soil_textures(
164    new_soil_textures: Vec<SoilTexture>,
165    conn: &mut AsyncPgConnection,
166) -> QueryResult<Vec<AreaDto>> {
167    let query = diesel::insert_into(soil_textures::table).values(&new_soil_textures);
168    debug!("{}", debug_query::<Pg, _>(&query));
169    let soil_textures = query
170        .get_results::<SoilTexture>(conn)
171        .await?
172        .into_iter()
173        .map(Into::into)
174        .collect::<Vec<AreaDto>>();
175    Ok(soil_textures)
176}
177
178/// Update shading areas in the database.
179///
180/// # Errors
181/// * Unknown, diesel doesn't say why it might error.
182pub async fn update_shadings(
183    updates: Vec<UpdateShading>,
184    conn: &mut AsyncPgConnection,
185) -> QueryResult<Vec<AreaDto>> {
186    conn.transaction(|transaction| {
187        Box::pin(async {
188            let mut results = Vec::with_capacity(updates.len());
189            for update in updates {
190                results.push(AreaDto::from(
191                    diesel::update(shadings::table.find(update.id))
192                        .set(update)
193                        .get_result::<Shading>(transaction)
194                        .await?,
195                ));
196            }
197            Ok(results) as QueryResult<Vec<AreaDto>>
198        })
199    })
200    .await
201}
202
203/// Update hydrology areas in the database.
204///
205/// # Errors
206/// * Unknown, diesel doesn't say why it might error.
207pub async fn update_hydrologies(
208    updates: Vec<UpdateHydrology>,
209    conn: &mut AsyncPgConnection,
210) -> QueryResult<Vec<AreaDto>> {
211    conn.transaction(|transaction| {
212        Box::pin(async {
213            let mut results = Vec::with_capacity(updates.len());
214            for update in updates {
215                results.push(AreaDto::from(
216                    diesel::update(hydrologies::table.find(update.id))
217                        .set(update)
218                        .get_result::<Hydrology>(transaction)
219                        .await?,
220                ));
221            }
222            Ok(results) as QueryResult<Vec<AreaDto>>
223        })
224    })
225    .await
226}
227
228/// Update soil texture areas in the database.
229///
230/// # Errors
231/// * Unknown, diesel doesn't say why it might error.
232pub async fn update_soil_textures(
233    updates: Vec<UpdateSoilTexture>,
234    conn: &mut AsyncPgConnection,
235) -> QueryResult<Vec<AreaDto>> {
236    conn.transaction(|transaction| {
237        Box::pin(async {
238            let mut results = Vec::with_capacity(updates.len());
239            for update in updates {
240                results.push(AreaDto::from(
241                    diesel::update(soil_textures::table.find(update.id))
242                        .set(update)
243                        .get_result::<SoilTexture>(transaction)
244                        .await?,
245                ));
246            }
247            Ok(results) as QueryResult<Vec<AreaDto>>
248        })
249    })
250    .await
251}
252
253/// Delete the shading from the database.
254///
255/// # Errors
256/// * Unknown, diesel doesn't say why it might error.
257pub async fn delete_areas_by_ids(
258    area_kind: AreaKind,
259    ids: Vec<Uuid>,
260    conn: &mut AsyncPgConnection,
261) -> QueryResult<usize> {
262    let result = match area_kind {
263        AreaKind::Shade => {
264            let query = diesel::delete(shadings::table.filter(shadings::id.eq_any(ids)));
265            debug!("{}", debug_query::<Pg, _>(&query));
266            query.execute(conn).await?
267        }
268        AreaKind::Hydrology => {
269            let query = diesel::delete(hydrologies::table.filter(hydrologies::id.eq_any(ids)));
270            debug!("{}", debug_query::<Pg, _>(&query));
271            query.execute(conn).await?
272        }
273        AreaKind::SoilTexture => {
274            let query = diesel::delete(soil_textures::table.filter(soil_textures::id.eq_any(ids)));
275            debug!("{}", debug_query::<Pg, _>(&query));
276            query.execute(conn).await?
277        }
278    };
279    Ok(result)
280}