1use super::*;
2use serde::de::{DeserializeSeed, Deserializer};
3#[derive(Debug, Getters, Clone, Ord, PartialEq, Eq, PartialOrd)]
5#[getset(get = "pub")]
6pub struct CibouletteSortingElement {
7 pub rel_chain: Vec<CibouletteResourceRelationshipDetails>,
9 pub direction: CibouletteSortingDirection,
11 pub field: ArcStr,
13}
14
15impl CibouletteSortingElement {
16 pub fn new(
18 rel_chain: Vec<CibouletteResourceRelationshipDetails>,
19 direction: CibouletteSortingDirection,
20 field: ArcStr,
21 ) -> Self {
22 CibouletteSortingElement {
23 rel_chain,
24 direction,
25 field,
26 }
27 }
28}
29
30#[derive(Debug, Getters)]
32#[getset(get = "pub")]
33pub struct CibouletteQueryParametersBuilder<'request> {
34 pub(super) include: Option<Vec<Vec<Cow<'request, str>>>>,
36 pub(super) sparse: BTreeMap<Cow<'request, str>, Vec<Cow<'request, str>>>,
38 pub(super) sorting: Vec<(CibouletteSortingDirection, Cow<'request, str>)>,
40 pub(super) page: BTreeMap<CiboulettePageType<'request>, Cow<'request, str>>,
42 pub(super) filter: Option<Cow<'request, str>>,
44 pub(super) filter_typed: BTreeMap<Cow<'request, str>, Cow<'request, str>>,
46 pub(super) meta: BTreeMap<Cow<'request, str>, Cow<'request, str>>,
48}
49
50#[derive(Debug, Getters, Default, Clone)]
52#[getset(get = "pub")]
53pub struct CibouletteQueryParameters<'request> {
54 pub include: Vec<Vec<CibouletteResourceRelationshipDetails>>,
55 pub sparse: BTreeMap<Arc<CibouletteResourceType>, Vec<ArcStr>>,
56 pub sorting: Vec<CibouletteSortingElement>,
57 pub page: BTreeMap<CiboulettePageType<'request>, Cow<'request, str>>,
58 pub filter: Option<Cow<'request, str>>,
59 pub filter_typed: BTreeMap<Cow<'request, str>, Cow<'request, str>>,
60 pub meta: BTreeMap<Cow<'request, str>, Cow<'request, str>>,
61}
62
63impl<'de> Deserialize<'de> for CibouletteQueryParametersBuilder<'de> {
64 #[inline]
65 fn deserialize<D>(deserializer: D) -> Result<CibouletteQueryParametersBuilder<'de>, D::Error>
66 where
67 D: Deserializer<'de>,
68 {
69 let visitor = CibouletteQueryParametersBuilderVisitor;
70
71 visitor.deserialize(deserializer)
72 }
73}
74
75impl<'request> CibouletteQueryParametersBuilder<'request> {
76 #[inline]
84 pub(super) fn check_relationship_exists(
85 store: &CibouletteStore,
86 main_type: &Arc<CibouletteResourceType>,
87 rel_list: &[Cow<'request, str>],
88 ) -> Result<Vec<CibouletteResourceRelationshipDetails>, CibouletteError> {
89 let mut current_type = main_type.clone();
90 let mut res: Vec<CibouletteResourceRelationshipDetails> = Vec::new();
91 let mut first = true;
92 for rel in rel_list {
93 if first {
94 first = false;
95 if current_type.name().as_str() == rel.as_ref() {
96 continue;
97 }
98 }
99 let tmp = current_type.get_relationship_details(store, rel)?.clone();
100 current_type = tmp.related_type().clone();
101 res.push(tmp);
102 }
103 if res.is_empty() {
104 return Err(CibouletteError::UnknownRelationship(
105 main_type.name().to_string(),
106 "<empty>".to_string(),
107 ));
108 }
109 Ok(res)
110 }
111
112 #[inline]
114 pub(super) fn check_field_exists(
115 type_: &Arc<CibouletteResourceType>,
116 field: &str,
117 ) -> Result<ArcStr, CibouletteError> {
118 match type_.schema().properties().get_key_value(field) {
119 Some((k, _)) => Ok(k.clone()),
120 None => Err(CibouletteError::UnknownField(
121 type_.name().to_string(),
122 field.to_string(),
123 )),
124 }
125 }
126
127 #[inline]
129 pub(super) fn check_fields_exists(
130 type_: &CibouletteResourceType,
131 field_list: Vec<Cow<'request, str>>,
132 ) -> Result<Vec<ArcStr>, CibouletteError> {
133 let curr_obj: &MessyJsonObject = type_.schema();
134 let mut res: Vec<ArcStr> = Vec::with_capacity(field_list.len());
135 let mut iter = field_list.iter().peekable();
136
137 while let Some(field) = iter.next() {
138 let (k, _) = curr_obj
139 .properties()
140 .get_key_value(field.as_ref())
141 .ok_or_else(|| {
142 CibouletteError::UnknownField(type_.name().to_string(), field.to_string())
143 })?;
144 res.push(k.clone());
145 match iter.peek().is_some() {
146 true => continue,
147 false => return Ok(res),
148 }
149 }
150 match field_list.len() {
151 0 => Err(CibouletteError::UnknownField(
152 type_.name().to_string(),
153 "<empty>".to_string(),
154 )),
155 _ => Err(CibouletteError::UnknownField(
156 type_.name().to_string(),
157 field_list.join("."),
158 )),
159 }
160 }
161
162 pub fn build(
164 self,
165 bag: &CibouletteStore,
166 main_type: Arc<CibouletteResourceType>,
167 ) -> Result<CibouletteQueryParameters<'request>, CibouletteError> {
168 let mut sparse: BTreeMap<Arc<CibouletteResourceType>, Vec<ArcStr>> = BTreeMap::new();
169 let mut sorting: Vec<CibouletteSortingElement> = Vec::with_capacity(self.sorting.len());
170
171 let include: Vec<Vec<CibouletteResourceRelationshipDetails>> = match self.include {
173 None => Vec::default(),
174 Some(include) => {
175 let mut res: Vec<Vec<CibouletteResourceRelationshipDetails>> = Vec::new();
176 for types in include.into_iter() {
177 res.push(Self::check_relationship_exists(
178 bag,
179 &main_type,
180 types.as_slice(),
181 )?);
182 }
183 res
184 }
185 };
186
187 for (type_, fields) in self.sparse.into_iter() {
189 let rel = bag.get_type(type_.as_ref())?;
190 let fields = match fields.is_empty() {
191 true => vec![],
192 false => Self::check_fields_exists(&rel, fields)?,
193 };
194 sparse.insert(rel.clone(), fields);
195 }
196
197 if !self.sorting.is_empty() {
199 for (direction, field) in self.sorting.into_iter() {
200 sorting.push(sorting::extract_type(
201 &bag,
202 main_type.clone(),
203 direction,
204 field,
205 )?)
206 }
207 }
208 let res = CibouletteQueryParameters {
209 include,
210 page: self.page,
211 meta: self.meta,
212 filter: self.filter,
213 filter_typed: self.filter_typed,
214 sparse,
215 sorting,
216 };
217 Ok(res)
218 }
219}