backend/model/entity/
drawing_properties_impl.rs

1use crate::model::dto::drawing_variant_impl::{BezierPolygon, DrawingVariantEntity, Freeline};
2use crate::model::entity::drawings::Drawing;
3
4use crate::model::entity::drawing_properties::{
5    BezierPolygonEntity, BezierPolygonPointEntity, EllipseEntity, FreeLineEntity,
6    FreeLinePointEntity, ImageEntity, LabelTextEntity, RectangleEntity,
7};
8use crate::model::r#enum::drawing_shape_type::DrawingShapeType;
9use crate::schema::{
10    bezier_polygon_points, bezier_polygon_props, ellipse_props, free_line_points, free_line_props,
11    image_props, label_text_props, rectangle_props,
12};
13use diesel::pg::Pg;
14use diesel::query_dsl::methods::FilterDsl;
15use diesel::{debug_query, ExpressionMethods, QueryDsl, QueryResult};
16use diesel_async::{AsyncPgConnection, RunQueryDsl};
17use log::debug;
18
19/// Insert properties for a newly created drawing.
20///
21/// Depending on the variant (`Rectangle`, `Ellipse`, `LabelText`, etc.),
22/// this inserts into the appropriate `*_props` table.
23///
24/// For complex shapes (`FreeLine` and `BezierPolygon`), this will:
25/// - Insert the props record into the main props table
26/// - Insert *all* associated point rows into the corresponding `*_point` table
27///
28/// # Errors
29/// * If `p.drawing_id` does not reference an existing drawing (FK violation)
30/// * If required fields in the props struct are missing or violate constraints
31pub async fn insert_props(
32    conn: &mut AsyncPgConnection,
33    props: &DrawingVariantEntity,
34) -> QueryResult<()> {
35    match props {
36        DrawingVariantEntity::Rectangle(p) => {
37            let query = diesel::insert_into(rectangle_props::table).values(p);
38            debug!("{}", debug_query::<Pg, _>(&query));
39            query.execute(conn).await?;
40        }
41
42        DrawingVariantEntity::Ellipse(p) => {
43            let query = diesel::insert_into(ellipse_props::table).values(p);
44            debug!("{}", debug_query::<Pg, _>(&query));
45            query.execute(conn).await?;
46        }
47
48        DrawingVariantEntity::FreeLine(f) => {
49            let query = diesel::insert_into(free_line_props::table).values(&f.freeline_props);
50            debug!("{}", debug_query::<Pg, _>(&query));
51            query.execute(conn).await?;
52
53            let query_points =
54                diesel::insert_into(free_line_points::table).values(&f.freeline_points);
55            debug!("{}", debug_query::<Pg, _>(&query_points));
56            query_points.execute(conn).await?;
57        }
58
59        DrawingVariantEntity::BezierPolygon(b) => {
60            let query = diesel::insert_into(bezier_polygon_props::table).values(&b.bezier_props);
61            debug!("{}", debug_query::<Pg, _>(&query));
62            query.execute(conn).await?;
63
64            let query_points =
65                diesel::insert_into(bezier_polygon_points::table).values(&b.bezier_points);
66            debug!("{}", debug_query::<Pg, _>(&query_points));
67            query_points.execute(conn).await?;
68        }
69
70        DrawingVariantEntity::LabelText(p) => {
71            let query = diesel::insert_into(label_text_props::table).values(p);
72            debug!("{}", debug_query::<Pg, _>(&query));
73            query.execute(conn).await?;
74        }
75
76        DrawingVariantEntity::Image(p) => {
77            let query = diesel::insert_into(image_props::table).values(p);
78            debug!("{}", debug_query::<Pg, _>(&query));
79            query.execute(conn).await?;
80        }
81    }
82
83    Ok(())
84}
85
86/// Load properties for a specific drawing.
87///
88/// This inspects `drawing.shape_type` and loads the corresponding props row(s)
89/// from the correct props table.
90///
91/// For `FreeLine` and `BezierPolygon`, this additionally:
92/// - Loads the ordered list of all point rows
93/// - Returns an aggregated `DrawingVariantEntity` containing both props and points
94///
95/// # Errors
96/// * If the props row for the drawing does not exist
97/// * If point rows fail to load or violate ordering assumptions
98pub async fn load_props_for(
99    drawing: &Drawing,
100    conn: &mut AsyncPgConnection,
101) -> QueryResult<DrawingVariantEntity> {
102    match drawing.shape_type {
103        DrawingShapeType::Rectangle => {
104            let query = rectangle_props::table.find(drawing.id);
105            debug!("{}", debug_query::<Pg, _>(&query));
106            let p: RectangleEntity = query.first(conn).await?;
107            Ok(DrawingVariantEntity::Rectangle(p))
108        }
109
110        DrawingShapeType::Ellipse => {
111            let query = ellipse_props::table.find(drawing.id);
112            debug!("{}", debug_query::<Pg, _>(&query));
113            let p: EllipseEntity = query.first(conn).await?;
114            Ok(DrawingVariantEntity::Ellipse(p))
115        }
116
117        DrawingShapeType::FreeLine => {
118            let query = free_line_props::table.find(drawing.id);
119            debug!("{}", debug_query::<Pg, _>(&query));
120            let props: FreeLineEntity = query.first(conn).await?;
121
122            let pts: Vec<FreeLinePointEntity> =
123                FilterDsl::filter(free_line_points::table, free_line_points::id.eq(drawing.id))
124                    .order(free_line_points::idx.asc())
125                    .load(conn)
126                    .await?;
127
128            Ok(DrawingVariantEntity::FreeLine(Freeline {
129                freeline_props: props,
130                freeline_points: pts,
131            }))
132        }
133
134        DrawingShapeType::BezierPolygon => {
135            let query = bezier_polygon_props::table.find(drawing.id);
136            debug!("{}", debug_query::<Pg, _>(&query));
137            let props: BezierPolygonEntity = query.first(conn).await?;
138
139            let pts: Vec<BezierPolygonPointEntity> = diesel::query_dsl::methods::FilterDsl::filter(
140                bezier_polygon_points::table,
141                bezier_polygon_points::id.eq(drawing.id),
142            )
143            .order(bezier_polygon_points::idx.asc())
144            .load(conn)
145            .await?;
146
147            Ok(DrawingVariantEntity::BezierPolygon(BezierPolygon {
148                bezier_props: props,
149                bezier_points: pts,
150            }))
151        }
152
153        DrawingShapeType::LabelText => {
154            let query = label_text_props::table.find(drawing.id);
155            debug!("{}", debug_query::<Pg, _>(&query));
156            let p: LabelTextEntity = query.first(conn).await?;
157            Ok(DrawingVariantEntity::LabelText(p))
158        }
159
160        DrawingShapeType::Image => {
161            let query = image_props::table.find(drawing.id);
162            debug!("{}", debug_query::<Pg, _>(&query));
163            let p: ImageEntity = query.first(conn).await?;
164            Ok(DrawingVariantEntity::Image(p))
165        }
166    }
167}
168
169/// Update an existing drawing's properties.
170///
171/// For complex shapes (`FreeLine`, `BezierPolygon`):
172/// - Updates the base props row
173/// - Deletes *all* existing point rows
174/// - Inserts the new point rows
175///
176/// # Errors
177/// * If the props row does not exist for the given drawing
178/// * If the update violates database constraints
179/// * If deletion or insertion of point rows fails
180pub async fn update_props(
181    conn: &mut AsyncPgConnection,
182    props: DrawingVariantEntity,
183) -> QueryResult<()> {
184    match props {
185        DrawingVariantEntity::Rectangle(p) => {
186            let query = diesel::update(rectangle_props::table.find(p.drawings_id)).set(&p);
187            debug!("{}", debug_query::<Pg, _>(&query));
188            query.execute(conn).await?;
189        }
190
191        DrawingVariantEntity::Ellipse(p) => {
192            let query = diesel::update(ellipse_props::table.find(p.drawings_id)).set(&p);
193            debug!("{}", debug_query::<Pg, _>(&query));
194            query.execute(conn).await?;
195        }
196
197        DrawingVariantEntity::LabelText(p) => {
198            let query = diesel::update(label_text_props::table.find(p.drawings_id)).set(&p);
199            debug!("{}", debug_query::<Pg, _>(&query));
200            query.execute(conn).await?;
201        }
202
203        DrawingVariantEntity::Image(p) => {
204            let query = diesel::update(image_props::table.find(p.drawings_id)).set(&p);
205            debug!("{}", debug_query::<Pg, _>(&query));
206            query.execute(conn).await?;
207        }
208
209        DrawingVariantEntity::FreeLine(f) => {
210            let query = diesel::update(free_line_props::table.find(f.freeline_props.drawings_id))
211                .set(&f.freeline_props);
212            debug!("{}", debug_query::<Pg, _>(&query));
213            query.execute(conn).await?;
214
215            let query_points = diesel::delete(FilterDsl::filter(
216                free_line_points::table,
217                free_line_points::id.eq(f.freeline_props.drawings_id),
218            ));
219            debug!("{}", debug_query::<Pg, _>(&query_points));
220            query_points.execute(conn).await?;
221
222            let query_insert =
223                diesel::insert_into(free_line_points::table).values(&f.freeline_points);
224
225            debug!("{}", debug_query::<Pg, _>(&query_insert));
226            query_insert.execute(conn).await?;
227        }
228
229        DrawingVariantEntity::BezierPolygon(b) => {
230            let query =
231                diesel::update(bezier_polygon_props::table.find(b.bezier_props.drawings_id))
232                    .set(&b.bezier_props);
233            debug!("{}", debug_query::<Pg, _>(&query));
234            query.execute(conn).await?;
235
236            let query_points = diesel::delete(FilterDsl::filter(
237                bezier_polygon_points::table,
238                bezier_polygon_points::id.eq(b.bezier_props.drawings_id),
239            ));
240            debug!("{}", debug_query::<Pg, _>(&query_points));
241            query_points.execute(conn).await?;
242
243            let query_insert =
244                diesel::insert_into(bezier_polygon_points::table).values(&b.bezier_points);
245            debug!("{}", debug_query::<Pg, _>(&query_insert));
246            query_insert.execute(conn).await?;
247        }
248    }
249
250    Ok(())
251}