1#![allow(clippy::missing_docs_in_private_items)]
7#![allow(clippy::missing_const_for_fn)]
9
10use chrono::NaiveDate;
11use postgis_diesel::types::{Point, Polygon};
12use serde::Serialize;
13use typeshare::typeshare;
14use uuid::Uuid;
15
16use crate::model::dto::{
17 areas::{AreaDto, AreaKind, AreaType},
18 base_layer_images::BaseLayerImageDto,
19 drawings::DrawingDto,
20 layers::{LayerDto, LayerRenameDto},
21 plantings::{
22 DeletePlantingDto, MovePlantingDto, PlantingDto, TransformPlantingDto,
23 UpdateAddDatePlantingDto, UpdatePlantingNoteDto, UpdateRemoveDatePlantingDto,
24 },
25 UpdateMapGeometryDto,
26};
27
28const SYSTEM_USER_ID: Uuid = Uuid::from_u128(0);
29
30#[typeshare]
31#[derive(Debug, Serialize, Clone)]
32#[serde(rename_all = "camelCase")]
33pub struct Action {
34 pub action_id: Uuid,
35 pub user_id: Uuid,
36 pub action: ActionType,
37}
38
39#[typeshare]
40#[derive(Debug, Serialize, Clone)]
41#[serde(tag = "type", content = "payload")]
44pub enum ActionType {
45 CreateLayer(LayerDto),
47 DeleteLayer(Uuid),
50 RestoreDrawingLayer(RestoreDrawingLayerActionPayload),
53 RenameLayer(LayerRenameDto),
55 ReorderLayers(Vec<Uuid>),
57
58 CreatePlanting(Vec<PlantingDto>),
60 DeletePlanting(Vec<DeletePlantingActionPayload>),
62 MovePlanting(Vec<MovePlantingActionPayload>),
64 TransformPlanting(Vec<TransformPlantingActionPayload>),
66 UpdatePlantingAddDate(Vec<UpdateAddDateActionPayload>),
68 UpdatePlantingRemoveDate(Vec<UpdateRemoveDateActionPayload>),
70 UpdatePlantingNotes(Vec<UpdatePlantingNotesActionPayload>),
72
73 CreateShading(Vec<AreaDto>),
75 DeleteShading(Vec<Uuid>),
77 UpdateShading(Vec<UpdateAreaActionPayload>),
79 UpdateShadingAddDate(Vec<UpdateAreaAddDateActionPayload>),
81 UpdateShadingRemoveDate(Vec<UpdateAreaRemoveDateActionPayload>),
83 UpdateShadingNotes(Vec<UpdateAreaNotesActionPayload>),
85
86 CreateHydrology(Vec<AreaDto>),
88 DeleteHydrology(Vec<Uuid>),
90 UpdateHydrology(Vec<UpdateAreaActionPayload>),
92 UpdateHydrologyAddDate(Vec<UpdateAreaAddDateActionPayload>),
94 UpdateHydrologyRemoveDate(Vec<UpdateAreaRemoveDateActionPayload>),
96 UpdateHydrologyNotes(Vec<UpdateAreaNotesActionPayload>),
98
99 CreateSoilTexture(Vec<AreaDto>),
101 DeleteSoilTexture(Vec<Uuid>),
103 UpdateSoilTexture(Vec<UpdateAreaActionPayload>),
105 UpdateSoilTextureAddDate(Vec<UpdateAreaAddDateActionPayload>),
107 UpdateSoilTextureRemoveDate(Vec<UpdateAreaRemoveDateActionPayload>),
109 UpdateSoilTextureNotes(Vec<UpdateAreaNotesActionPayload>),
111
112 CreateBaseLayerImage(CreateBaseLayerImageActionPayload),
114 UpdateBaseLayerImage(UpdateBaseLayerImageActionPayload),
116 DeleteBaseLayerImage(DeleteBaseLayerImageActionPayload),
118 UpdateMapGeometry(UpdateMapGeometryActionPayload),
120 UpdatePlantingAdditionalName(UpdatePlantingAdditionalNamePayload),
122
123 CreateDrawing(Vec<DrawingDto>),
125 DeleteDrawing(Vec<Uuid>),
127 UpdateDrawing(Vec<DrawingDto>),
129 UpdateDrawingAddDate(Vec<DrawingDto>),
131 UpdateDrawingRemoveDate(Vec<DrawingDto>),
133 UpdateDrawingNotes(Vec<UpdateDrawingNotesActionPayload>),
135
136 ServerShutdown(String),
138}
139
140impl Action {
141 #[must_use]
142 pub fn new_create_planting_action(
143 dtos: Vec<PlantingDto>,
144 user_id: Uuid,
145 action_id: Uuid,
146 ) -> Self {
147 Self {
148 action_id,
149 user_id,
150 action: ActionType::CreatePlanting(dtos),
151 }
152 }
153
154 #[must_use]
155 pub fn new_delete_planting_action(
156 dtos: &[DeletePlantingDto],
157 user_id: Uuid,
158 action_id: Uuid,
159 ) -> Self {
160 Self {
161 action_id,
162 user_id,
163 action: ActionType::DeletePlanting(
164 dtos.iter()
165 .map(|dto| DeletePlantingActionPayload { id: dto.id })
166 .collect(),
167 ),
168 }
169 }
170
171 #[must_use]
172 pub fn new_move_planting_action(
173 dtos: &[MovePlantingDto],
174 user_id: Uuid,
175 action_id: Uuid,
176 ) -> Self {
177 Self {
178 action_id,
179 user_id,
180 action: ActionType::MovePlanting(
181 dtos.iter()
182 .map(|dto| MovePlantingActionPayload {
183 id: dto.id,
184 x: dto.x,
185 y: dto.y,
186 })
187 .collect(),
188 ),
189 }
190 }
191
192 #[must_use]
193 pub fn new_transform_planting_action(
194 dtos: &[TransformPlantingDto],
195 user_id: Uuid,
196 action_id: Uuid,
197 ) -> Self {
198 Self {
199 action_id,
200 user_id,
201 action: ActionType::TransformPlanting(
202 dtos.iter()
203 .map(|dto| TransformPlantingActionPayload {
204 id: dto.id,
205 x: dto.x,
206 y: dto.y,
207 rotation: dto.rotation,
208 size_x: dto.size_x,
209 size_y: dto.size_y,
210 height: dto.height,
211 })
212 .collect(),
213 ),
214 }
215 }
216
217 #[must_use]
218 pub fn new_update_planting_add_date_action(
219 dtos: &[UpdateAddDatePlantingDto],
220 user_id: Uuid,
221 action_id: Uuid,
222 ) -> Self {
223 Self {
224 action_id,
225 user_id,
226 action: ActionType::UpdatePlantingAddDate(
227 dtos.iter()
228 .map(|dto| UpdateAddDateActionPayload {
229 id: dto.id,
230 add_date: dto.add_date,
231 })
232 .collect(),
233 ),
234 }
235 }
236
237 #[must_use]
238 pub fn new_update_planting_remove_date_action(
239 dtos: &[UpdateRemoveDatePlantingDto],
240 user_id: Uuid,
241 action_id: Uuid,
242 ) -> Self {
243 Self {
244 action_id,
245 user_id,
246 action: ActionType::UpdatePlantingRemoveDate(
247 dtos.iter()
248 .map(|dto| UpdateRemoveDateActionPayload {
249 id: dto.id,
250 remove_date: dto.remove_date,
251 })
252 .collect(),
253 ),
254 }
255 }
256
257 #[must_use]
258 pub fn new_update_planting_note_action(
259 dtos: &[UpdatePlantingNoteDto],
260 user_id: Uuid,
261 action_id: Uuid,
262 ) -> Self {
263 Self {
264 action_id,
265 user_id,
266 action: ActionType::UpdatePlantingNotes(
267 dtos.iter()
268 .map(|dto| UpdatePlantingNotesActionPayload {
269 id: dto.id,
270 notes: dto.notes.clone(),
271 })
272 .collect(),
273 ),
274 }
275 }
276
277 #[must_use]
278 pub fn new_create_area_action(
279 area_kind: AreaKind,
280 dtos: Vec<AreaDto>,
281 user_id: Uuid,
282 action_id: Uuid,
283 ) -> Self {
284 Self {
285 action_id,
286 user_id,
287 action: match area_kind {
288 AreaKind::Shade => ActionType::CreateShading(dtos),
289 AreaKind::Hydrology => ActionType::CreateHydrology(dtos),
290 AreaKind::SoilTexture => ActionType::CreateSoilTexture(dtos),
291 },
292 }
293 }
294
295 #[must_use]
296 pub fn new_update_area_action(
297 area_kind: AreaKind,
298 dtos: &[AreaDto],
299 user_id: Uuid,
300 action_id: Uuid,
301 ) -> Self {
302 let payload = dtos
303 .iter()
304 .map(|dto| UpdateAreaActionPayload {
305 id: dto.id,
306 area_type: dto.area_type.clone(),
307 })
308 .collect();
309 Self {
310 action_id,
311 user_id,
312 action: match area_kind {
313 AreaKind::Shade => ActionType::UpdateShading(payload),
314 AreaKind::Hydrology => ActionType::UpdateHydrology(payload),
315 AreaKind::SoilTexture => ActionType::UpdateSoilTexture(payload),
316 },
317 }
318 }
319
320 #[must_use]
321 pub fn new_update_area_add_date_action(
322 area_kind: AreaKind,
323 dtos: &[AreaDto],
324 user_id: Uuid,
325 action_id: Uuid,
326 ) -> Self {
327 let payload = dtos
328 .iter()
329 .map(|dto| UpdateAreaAddDateActionPayload {
330 id: dto.id,
331 add_date: dto.add_date,
332 })
333 .collect();
334 Self {
335 action_id,
336 user_id,
337 action: match area_kind {
338 AreaKind::Shade => ActionType::UpdateShadingAddDate(payload),
339 AreaKind::Hydrology => ActionType::UpdateHydrologyAddDate(payload),
340 AreaKind::SoilTexture => ActionType::UpdateSoilTextureAddDate(payload),
341 },
342 }
343 }
344
345 #[must_use]
346 pub fn new_update_area_remove_date_action(
347 area_kind: AreaKind,
348 dtos: &[AreaDto],
349 user_id: Uuid,
350 action_id: Uuid,
351 ) -> Self {
352 let payload = dtos
353 .iter()
354 .map(|dto| UpdateAreaRemoveDateActionPayload {
355 id: dto.id,
356 remove_date: dto.remove_date,
357 })
358 .collect();
359 Self {
360 action_id,
361 user_id,
362 action: match area_kind {
363 AreaKind::Shade => ActionType::UpdateShadingRemoveDate(payload),
364 AreaKind::Hydrology => ActionType::UpdateHydrologyRemoveDate(payload),
365 AreaKind::SoilTexture => ActionType::UpdateSoilTextureRemoveDate(payload),
366 },
367 }
368 }
369
370 #[must_use]
371 pub fn new_update_area_notes_action(
372 area_kind: AreaKind,
373 dtos: &[AreaDto],
374 user_id: Uuid,
375 action_id: Uuid,
376 ) -> Self {
377 let payload: Vec<UpdateAreaNotesActionPayload> = dtos
378 .iter()
379 .map(|dto| UpdateAreaNotesActionPayload {
380 id: dto.id,
381 notes: dto.notes.clone(),
382 })
383 .collect();
384 Self {
385 action_id,
386 user_id,
387 action: match area_kind {
388 AreaKind::Shade => ActionType::UpdateShadingNotes(payload),
389 AreaKind::Hydrology => ActionType::UpdateHydrologyNotes(payload),
390 AreaKind::SoilTexture => ActionType::UpdateSoilTextureNotes(payload),
391 },
392 }
393 }
394
395 #[must_use]
396 pub fn new_delete_area_action(
397 area_kind: AreaKind,
398 ids: Vec<Uuid>,
399 user_id: Uuid,
400 action_id: Uuid,
401 ) -> Self {
402 Self {
403 action_id,
404 user_id,
405 action: match area_kind {
406 AreaKind::Shade => ActionType::DeleteShading(ids),
407 AreaKind::Hydrology => ActionType::DeleteHydrology(ids),
408 AreaKind::SoilTexture => ActionType::DeleteSoilTexture(ids),
409 },
410 }
411 }
412
413 #[must_use]
414 pub fn new_shutdown_signal_action(message: String) -> Self {
415 Self {
416 action_id: Uuid::now_v7(),
417 user_id: SYSTEM_USER_ID,
418 action: ActionType::ServerShutdown(message),
419 }
420 }
421}
422
423#[typeshare]
424#[derive(Debug, Serialize, Clone)]
425#[serde(rename_all = "camelCase")]
427pub struct RestoreDrawingLayerActionPayload {
428 id: Uuid,
430 drawings: Vec<DrawingDto>,
432}
433
434impl RestoreDrawingLayerActionPayload {
435 #[must_use]
436 pub fn new(id: Uuid, drawings: Vec<DrawingDto>) -> Self {
437 Self { id, drawings }
438 }
439}
440
441#[typeshare]
442#[derive(Debug, Serialize, Clone)]
443#[serde(rename_all = "camelCase")]
446pub struct CreatePlantActionPayload {
447 id: Uuid,
448 layer_id: Uuid,
449 plant_id: i32,
450 x: i32,
451 y: i32,
452 rotation: i32,
453 size_x: i32,
454 size_y: i32,
455 height: Option<i32>,
456 add_date: Option<NaiveDate>,
457 remove_date: Option<NaiveDate>,
458 seed_id: Option<i32>,
459 additional_name: Option<String>,
460 is_area: bool,
461}
462
463#[typeshare]
464#[derive(Debug, Serialize, Clone)]
465#[serde(rename_all = "camelCase")]
467pub struct DeletePlantingActionPayload {
468 id: Uuid,
469}
470
471#[typeshare]
472#[derive(Debug, Serialize, Clone)]
473#[serde(rename_all = "camelCase")]
475pub struct MovePlantingActionPayload {
476 id: Uuid,
477 x: i32,
478 y: i32,
479}
480
481#[typeshare]
482#[derive(Debug, Serialize, Clone)]
483#[serde(rename_all = "camelCase")]
485pub struct TransformPlantingActionPayload {
486 id: Uuid,
487 x: i32,
488 y: i32,
489 rotation: i32,
490 size_x: i32,
491 size_y: i32,
492 height: Option<i32>,
493}
494
495#[typeshare]
496#[derive(Debug, Serialize, Clone)]
497#[serde(rename_all = "camelCase")]
498pub struct CreateShadingActionPayload {
499 id: Uuid,
500 layer_id: Uuid,
501 area_type: AreaType,
502 geometry: Polygon<Point>,
503 add_date: Option<NaiveDate>,
504 remove_date: Option<NaiveDate>,
505 notes: String,
506}
507
508#[typeshare]
509#[derive(Debug, Serialize, Clone)]
510#[serde(rename_all = "camelCase")]
511pub struct CreateHydrologyActionPayload {
512 id: Uuid,
513 layer_id: Uuid,
514 area_type: AreaType,
515 geometry: Polygon<Point>,
516 add_date: Option<NaiveDate>,
517 remove_date: Option<NaiveDate>,
518 notes: String,
519}
520
521#[typeshare]
522#[derive(Debug, Serialize, Clone)]
523#[serde(rename_all = "camelCase")]
524pub struct CreateAreaActionPayload {
525 id: Uuid,
526 layer_id: Uuid,
527 area_type: AreaType,
528 geometry: Polygon<Point>,
529 add_date: Option<NaiveDate>,
530 remove_date: Option<NaiveDate>,
531 notes: String,
532}
533
534impl CreateAreaActionPayload {
535 #[must_use]
536 pub fn new(payload: AreaDto) -> Self {
537 Self {
538 id: payload.id,
539 layer_id: payload.layer_id,
540 area_type: payload.area_type,
541 geometry: payload.geometry,
542 add_date: payload.add_date,
543 remove_date: payload.remove_date,
544 notes: payload.notes,
545 }
546 }
547}
548
549#[typeshare]
550#[derive(Debug, Serialize, Clone)]
551#[serde(rename_all = "camelCase")]
553pub struct UpdateAreaActionPayload {
554 id: Uuid,
555 area_type: AreaType,
556}
557
558impl UpdateAreaActionPayload {
559 #[must_use]
560 pub fn new(payload: AreaDto) -> Self {
561 Self {
562 id: payload.id,
563 area_type: payload.area_type,
564 }
565 }
566}
567
568#[typeshare]
569#[derive(Debug, Serialize, Clone)]
570#[serde(rename_all = "camelCase")]
572pub struct UpdateAreaAddDateActionPayload {
573 id: Uuid,
574 add_date: Option<NaiveDate>,
575}
576
577impl UpdateAreaAddDateActionPayload {
578 #[must_use]
579 pub fn new(payload: &AreaDto) -> Self {
580 Self {
581 id: payload.id,
582 add_date: payload.add_date,
583 }
584 }
585}
586
587#[typeshare]
588#[derive(Debug, Serialize, Clone)]
589#[serde(rename_all = "camelCase")]
591pub struct UpdateAreaRemoveDateActionPayload {
592 id: Uuid,
593 remove_date: Option<NaiveDate>,
594}
595
596impl UpdateAreaRemoveDateActionPayload {
597 #[must_use]
598 pub fn new(payload: &AreaDto) -> Self {
599 Self {
600 id: payload.id,
601 remove_date: payload.remove_date,
602 }
603 }
604}
605
606#[typeshare]
607#[derive(Debug, Serialize, Clone)]
608pub struct UpdateAreaNotesActionPayload {
610 id: Uuid,
611 notes: String,
612}
613
614impl UpdateAreaNotesActionPayload {
615 #[must_use]
616 pub fn new(payload: &AreaDto) -> Self {
617 Self {
618 id: payload.id,
619 notes: payload.notes.clone(),
620 }
621 }
622}
623
624#[typeshare]
625#[derive(Debug, Serialize, Clone)]
626#[serde(rename_all = "camelCase")]
628pub struct UpdatePlantingNotesActionPayload {
629 id: Uuid,
630 notes: String,
631}
632
633#[typeshare]
634#[derive(Debug, Serialize, Clone)]
635#[serde(rename_all = "camelCase")]
638pub struct CreateBaseLayerImageActionPayload {
639 id: Uuid,
640 layer_id: Uuid,
641 rotation: i32,
642 scale: i32,
643 path: String,
644}
645
646impl CreateBaseLayerImageActionPayload {
647 #[must_use]
648 pub fn new(payload: BaseLayerImageDto) -> Self {
649 Self {
650 id: payload.id,
651 layer_id: payload.layer_id,
652 rotation: payload.rotation,
653 scale: payload.scale,
654 path: payload.path,
655 }
656 }
657}
658
659#[typeshare]
660#[derive(Debug, Serialize, Clone)]
661#[serde(rename_all = "camelCase")]
663pub struct DeleteBaseLayerImageActionPayload {
664 id: Uuid,
665}
666
667impl DeleteBaseLayerImageActionPayload {
668 #[must_use]
669 pub fn new(id: Uuid) -> Self {
670 Self { id }
671 }
672}
673
674#[typeshare]
675#[derive(Debug, Serialize, Clone)]
676#[serde(rename_all = "camelCase")]
678pub struct UpdateBaseLayerImageActionPayload {
679 id: Uuid,
680 layer_id: Uuid,
681 rotation: i32,
682 scale: i32,
683 path: String,
684 x: i32,
685 y: i32,
686}
687
688impl UpdateBaseLayerImageActionPayload {
689 #[must_use]
690 pub fn new(payload: BaseLayerImageDto) -> Self {
691 Self {
692 id: payload.id,
693 layer_id: payload.layer_id,
694 rotation: payload.rotation,
695 scale: payload.scale,
696 path: payload.path,
697 x: payload.x,
698 y: payload.y,
699 }
700 }
701}
702
703#[typeshare]
704#[derive(Debug, Serialize, Clone)]
705#[serde(rename_all = "camelCase")]
707pub struct UpdateAddDateActionPayload {
708 id: Uuid,
709 add_date: Option<NaiveDate>,
710}
711
712#[typeshare]
713#[derive(Debug, Serialize, Clone)]
714#[serde(rename_all = "camelCase")]
716pub struct UpdateRemoveDateActionPayload {
717 id: Uuid,
718 remove_date: Option<NaiveDate>,
719}
720
721#[typeshare]
722#[derive(Debug, Serialize, Clone)]
723#[serde(rename_all = "camelCase")]
725pub struct UpdateMapGeometryActionPayload {
726 map_id: i32,
728 #[typeshare(serialized_as = "object")]
730 geometry: Polygon<Point>,
731}
732
733impl UpdateMapGeometryActionPayload {
734 #[must_use]
735 pub fn new(payload: UpdateMapGeometryDto, map_id: i32) -> Self {
736 Self {
737 map_id,
738 geometry: payload.geometry,
739 }
740 }
741}
742
743#[typeshare]
744#[derive(Debug, Serialize, Clone)]
745#[serde(rename_all = "camelCase")]
747pub struct UpdatePlantingAdditionalNamePayload {
748 id: Uuid,
749 additional_name: Option<String>,
750}
751
752impl UpdatePlantingAdditionalNamePayload {
753 #[must_use]
754 pub fn new(payload: &PlantingDto, new_additional_name: Option<String>) -> Self {
755 Self {
756 id: payload.id,
757 additional_name: new_additional_name,
758 }
759 }
760}
761
762#[typeshare]
763#[derive(Debug, Serialize, Clone)]
764#[serde(rename_all = "camelCase")]
766pub struct UpdateDrawingNotesActionPayload {
767 id: Uuid,
768 notes: String,
769}
770
771impl UpdateDrawingNotesActionPayload {
772 #[must_use]
773 pub fn new(drawing: DrawingDto) -> Self {
774 Self {
775 id: drawing.id,
776 notes: drawing.notes,
777 }
778 }
779}