postrust_core/api_request/
preferences.rs1use super::types::*;
6use crate::error::{Error, Result};
7use http::HeaderMap;
8
9pub fn parse_preferences(headers: &HeaderMap) -> Result<Preferences> {
11 let mut prefs = Preferences::default();
12
13 let prefer = match headers.get("prefer") {
14 Some(v) => v.to_str().map_err(|_| Error::InvalidHeader("Prefer"))?,
15 None => return Ok(prefs),
16 };
17
18 for pref in prefer.split(',').map(|s| s.trim()) {
19 parse_preference(&mut prefs, pref);
20 }
21
22 Ok(prefs)
23}
24
25fn parse_preference(prefs: &mut Preferences, pref: &str) {
26 let pref = pref.trim();
27
28 if let Some((key, value)) = pref.split_once('=') {
30 let key = key.trim();
31 let value = value.trim().trim_matches('"');
32
33 match key {
34 "resolution" => {
35 prefs.resolution = match value {
36 "merge-duplicates" => Some(PreferResolution::MergeDuplicates),
37 "ignore-duplicates" => Some(PreferResolution::IgnoreDuplicates),
38 _ => None,
39 };
40 }
41 "return" => {
42 prefs.representation = match value {
43 "representation" => PreferRepresentation::Full,
44 "headers-only" => PreferRepresentation::HeadersOnly,
45 "minimal" => PreferRepresentation::None,
46 _ => PreferRepresentation::None,
47 };
48 }
49 "count" => {
50 prefs.count = match value {
51 "exact" => Some(PreferCount::Exact),
52 "planned" => Some(PreferCount::Planned),
53 "estimated" => Some(PreferCount::Estimated),
54 _ => None,
55 };
56 }
57 "tx" => {
58 prefs.transaction = match value {
59 "commit" => PreferTransaction::Commit,
60 "rollback" => PreferTransaction::Rollback,
61 _ => PreferTransaction::Commit,
62 };
63 }
64 "missing" => {
65 prefs.missing = match value {
66 "default" => PreferMissing::ApplyDefaults,
67 "null" => PreferMissing::ApplyNulls,
68 _ => PreferMissing::ApplyDefaults,
69 };
70 }
71 "handling" => {
72 prefs.handling = match value {
73 "strict" => PreferHandling::Strict,
74 "lenient" => PreferHandling::Lenient,
75 _ => PreferHandling::Strict,
76 };
77 }
78 "timezone" => {
79 prefs.timezone = Some(value.to_string());
80 }
81 "max-affected" => {
82 if let Ok(n) = value.parse::<i64>() {
83 prefs.max_affected = Some(n);
84 }
85 }
86 _ => {
87 prefs.invalid.push(pref.to_string());
88 }
89 }
90 return;
91 }
92
93 match pref {
95 "return=representation" => prefs.representation = PreferRepresentation::Full,
96 "return=headers-only" => prefs.representation = PreferRepresentation::HeadersOnly,
97 "return=minimal" => prefs.representation = PreferRepresentation::None,
98 "count=exact" => prefs.count = Some(PreferCount::Exact),
99 "count=planned" => prefs.count = Some(PreferCount::Planned),
100 "count=estimated" => prefs.count = Some(PreferCount::Estimated),
101 "resolution=merge-duplicates" => prefs.resolution = Some(PreferResolution::MergeDuplicates),
102 "resolution=ignore-duplicates" => {
103 prefs.resolution = Some(PreferResolution::IgnoreDuplicates)
104 }
105 "tx=commit" => prefs.transaction = PreferTransaction::Commit,
106 "tx=rollback" => prefs.transaction = PreferTransaction::Rollback,
107 "params=single-object" => {} "params=multiple-objects" => {}
109 _ => {
110 prefs.invalid.push(pref.to_string());
111 }
112 }
113}
114
115pub fn preference_applied(prefs: &Preferences) -> Option<String> {
117 let mut applied = Vec::new();
118
119 if prefs.resolution.is_some() {
120 let val = match prefs.resolution {
121 Some(PreferResolution::MergeDuplicates) => "resolution=merge-duplicates",
122 Some(PreferResolution::IgnoreDuplicates) => "resolution=ignore-duplicates",
123 None => "",
124 };
125 if !val.is_empty() {
126 applied.push(val);
127 }
128 }
129
130 match prefs.representation {
131 PreferRepresentation::Full => applied.push("return=representation"),
132 PreferRepresentation::HeadersOnly => applied.push("return=headers-only"),
133 PreferRepresentation::None => {}
134 }
135
136 if let Some(count) = &prefs.count {
137 let val = match count {
138 PreferCount::Exact => "count=exact",
139 PreferCount::Planned => "count=planned",
140 PreferCount::Estimated => "count=estimated",
141 };
142 applied.push(val);
143 }
144
145 match prefs.transaction {
146 PreferTransaction::Rollback => applied.push("tx=rollback"),
147 PreferTransaction::Commit => {}
148 }
149
150 if applied.is_empty() {
151 None
152 } else {
153 Some(applied.join(", "))
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160 use http::HeaderValue;
161
162 fn headers_with_prefer(value: &str) -> HeaderMap {
163 let mut headers = HeaderMap::new();
164 headers.insert("prefer", HeaderValue::from_str(value).unwrap());
165 headers
166 }
167
168 #[test]
169 fn test_parse_return_representation() {
170 let headers = headers_with_prefer("return=representation");
171 let prefs = parse_preferences(&headers).unwrap();
172 assert_eq!(prefs.representation, PreferRepresentation::Full);
173 }
174
175 #[test]
176 fn test_parse_count_exact() {
177 let headers = headers_with_prefer("count=exact");
178 let prefs = parse_preferences(&headers).unwrap();
179 assert_eq!(prefs.count, Some(PreferCount::Exact));
180 }
181
182 #[test]
183 fn test_parse_resolution() {
184 let headers = headers_with_prefer("resolution=merge-duplicates");
185 let prefs = parse_preferences(&headers).unwrap();
186 assert_eq!(prefs.resolution, Some(PreferResolution::MergeDuplicates));
187 }
188
189 #[test]
190 fn test_parse_multiple() {
191 let headers = headers_with_prefer("return=representation, count=exact, tx=rollback");
192 let prefs = parse_preferences(&headers).unwrap();
193 assert_eq!(prefs.representation, PreferRepresentation::Full);
194 assert_eq!(prefs.count, Some(PreferCount::Exact));
195 assert_eq!(prefs.transaction, PreferTransaction::Rollback);
196 }
197
198 #[test]
199 fn test_parse_timezone() {
200 let headers = headers_with_prefer("timezone=America/New_York");
201 let prefs = parse_preferences(&headers).unwrap();
202 assert_eq!(prefs.timezone, Some("America/New_York".to_string()));
203 }
204
205 #[test]
206 fn test_parse_max_affected() {
207 let headers = headers_with_prefer("max-affected=100");
208 let prefs = parse_preferences(&headers).unwrap();
209 assert_eq!(prefs.max_affected, Some(100));
210 }
211
212 #[test]
213 fn test_preference_applied() {
214 let mut prefs = Preferences::default();
215 prefs.representation = PreferRepresentation::Full;
216 prefs.count = Some(PreferCount::Exact);
217
218 let applied = preference_applied(&prefs).unwrap();
219 assert!(applied.contains("return=representation"));
220 assert!(applied.contains("count=exact"));
221 }
222}