shaperail_runtime/db/
generated.rs1use serde::de::DeserializeOwned;
2use serde::Serialize;
3use serde_json::{Map, Value};
4
5use super::{FilterSet, ResourceRow, SortDirection, SortParam};
6use shaperail_core::{FieldError, ShaperailError};
7
8pub fn parse_optional_json<T: DeserializeOwned>(
9 data: &Map<String, Value>,
10 field: &str,
11) -> Result<Option<T>, ShaperailError> {
12 let Some(value) = data.get(field) else {
13 return Ok(None);
14 };
15
16 if value.is_null() {
17 return Ok(None);
18 }
19
20 serde_json::from_value(value.clone())
21 .map(Some)
22 .map_err(|_| {
23 ShaperailError::Validation(vec![FieldError {
24 field: field.to_string(),
25 message: format!("{field} has an invalid value"),
26 code: "invalid_value".to_string(),
27 }])
28 })
29}
30
31pub fn parse_embedded_json<T: DeserializeOwned>(
32 field: &str,
33 value: Value,
34) -> Result<T, ShaperailError> {
35 serde_json::from_value(value).map_err(|_| {
36 ShaperailError::Internal(format!(
37 "Invalid generated default value for field '{field}'"
38 ))
39 })
40}
41
42pub fn require_field<T>(value: Option<T>, field: &str) -> Result<T, ShaperailError> {
43 value.ok_or_else(|| {
44 ShaperailError::Validation(vec![FieldError {
45 field: field.to_string(),
46 message: format!("{field} is required"),
47 code: "required".to_string(),
48 }])
49 })
50}
51
52pub fn parse_filter_text(filters: &FilterSet, field: &str) -> Option<String> {
53 filters
54 .filters
55 .iter()
56 .find(|filter| filter.field == field)
57 .map(|filter| filter.value.clone())
58}
59
60pub fn parse_filter<T>(
61 filters: &FilterSet,
62 field: &str,
63 code: &str,
64 parser: impl Fn(&str) -> Result<T, ShaperailError>,
65) -> Result<Option<T>, ShaperailError> {
66 let Some(raw) = parse_filter_text(filters, field) else {
67 return Ok(None);
68 };
69
70 parser(&raw).map(Some).map_err(|_| {
71 ShaperailError::Validation(vec![FieldError {
72 field: field.to_string(),
73 message: format!("{field} has an invalid filter value"),
74 code: code.to_string(),
75 }])
76 })
77}
78
79pub fn sort_field_at(sort: &SortParam, index: usize) -> Option<String> {
80 sort.fields.get(index).map(|field| field.field.clone())
81}
82
83pub fn sort_direction_at(sort: &SortParam, index: usize) -> Option<&'static str> {
84 sort.fields.get(index).map(|field| match field.direction {
85 SortDirection::Asc => "asc",
86 SortDirection::Desc => "desc",
87 })
88}
89
90pub fn row_from_model<T: Serialize>(model: &T) -> Result<ResourceRow, ShaperailError> {
91 serde_json::to_value(model)
92 .map(ResourceRow)
93 .map_err(|e| ShaperailError::Internal(format!("Failed to serialize row: {e}")))
94}