azure_functions/bindings/
cosmos_db_document.rs1use crate::{
2 http::Body,
3 rpc::{typed_data::Data, TypedData},
4 util::convert_from,
5 FromVec, IntoVec,
6};
7use serde_json::{from_str, Map, Value};
8use std::borrow::Cow;
9use std::fmt;
10
11#[derive(Debug, Clone)]
100pub struct CosmosDbDocument(Value);
101
102impl CosmosDbDocument {
103 pub fn new(value: Value) -> CosmosDbDocument {
107 if !value.is_object() {
108 panic!("expected a single object for a Cosmos DB document");
109 }
110 CosmosDbDocument(value)
111 }
112
113 pub fn is_null(&self) -> bool {
117 self.0.is_null()
118 }
119
120 pub fn as_object(&self) -> Option<&Map<String, Value>> {
124 self.0.as_object()
125 }
126
127 pub fn as_object_mut(&mut self) -> Option<&mut Map<String, Value>> {
131 self.0.as_object_mut()
132 }
133}
134
135impl fmt::Display for CosmosDbDocument {
136 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
137 self.0.fmt(f)
138 }
139}
140
141impl<'a> From<&'a str> for CosmosDbDocument {
142 fn from(json: &'a str) -> Self {
143 CosmosDbDocument::new(from_str(json).unwrap())
144 }
145}
146
147impl From<String> for CosmosDbDocument {
148 fn from(json: String) -> Self {
149 CosmosDbDocument::new(from_str(&json).unwrap())
150 }
151}
152
153impl From<Value> for CosmosDbDocument {
154 fn from(value: Value) -> Self {
155 CosmosDbDocument::new(value)
156 }
157}
158
159#[doc(hidden)]
160impl IntoVec<CosmosDbDocument> for TypedData {
161 fn into_vec(self) -> Vec<CosmosDbDocument> {
162 if self.data.is_none() {
163 return vec![];
164 }
165
166 match convert_from(&self).expect("expected JSON data for Cosmos DB document") {
167 Value::Null => vec![],
168 Value::Array(arr) => arr.into_iter().map(CosmosDbDocument::new).collect(),
169 Value::Object(obj) => vec![CosmosDbDocument(Value::Object(obj))],
170 _ => panic!("expected array or object for Cosmos DB document data"),
171 }
172 }
173}
174
175#[doc(hidden)]
176impl FromVec<CosmosDbDocument> for TypedData {
177 fn from_vec(vec: Vec<CosmosDbDocument>) -> Self {
178 TypedData {
179 data: Some(Data::Json(
180 Value::Array(vec.into_iter().map(|d| d.0).collect()).to_string(),
181 )),
182 }
183 }
184}
185
186#[doc(hidden)]
187impl From<TypedData> for CosmosDbDocument {
188 fn from(data: TypedData) -> Self {
189 if data.data.is_none() {
190 return CosmosDbDocument(Value::Null);
191 }
192
193 let value: Value = convert_from(&data).expect("expected JSON data for Cosmos DB document");
194
195 match value {
196 Value::Null => CosmosDbDocument(Value::Null),
197 Value::Array(mut arr) => {
198 if arr.is_empty() {
199 CosmosDbDocument(Value::Null)
200 } else {
201 CosmosDbDocument::new(arr.swap_remove(0))
202 }
203 }
204 Value::Object(obj) => CosmosDbDocument(Value::Object(obj)),
205 _ => panic!("expected an array or object for Cosmos DB document data"),
206 }
207 }
208}
209
210impl Into<String> for CosmosDbDocument {
211 fn into(self) -> String {
212 self.0.to_string()
213 }
214}
215
216impl Into<Value> for CosmosDbDocument {
217 fn into(self) -> Value {
218 self.0
219 }
220}
221
222impl<'a> Into<Body<'a>> for CosmosDbDocument {
223 fn into(self) -> Body<'a> {
224 self.0.into()
225 }
226}
227
228impl<'a> Into<Body<'a>> for Vec<CosmosDbDocument> {
229 fn into(self) -> Body<'a> {
230 Body::Json(Cow::from(
231 Value::Array(self.into_iter().map(|d| d.0).collect()).to_string(),
232 ))
233 }
234}
235
236#[doc(hidden)]
237impl Into<TypedData> for CosmosDbDocument {
238 fn into(self) -> TypedData {
239 TypedData {
240 data: Some(Data::Json(self.0.to_string())),
241 }
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248 use serde_json::json;
249
250 #[test]
251 fn it_constructs_from_an_object_value() {
252 let document = CosmosDbDocument::new(json!({ "id": "foo", "key": "value"}));
253 let data = document.as_object().unwrap();
254 assert_eq!(data["id"].as_str().unwrap(), "foo");
255 assert_eq!(data["key"].as_str().unwrap(), "value");
256 }
257
258 #[test]
259 #[should_panic(expected = "expected a single object for a Cosmos DB document")]
260 fn it_panics_if_constructed_without_an_object_or_array() {
261 CosmosDbDocument::new(json!(5));
262 }
263
264 #[test]
265 fn it_displays_as_json() {
266 let document = CosmosDbDocument::new(json!({ "foo": "bar"}));
267 assert_eq!(format!("{}", document), r#"{"foo":"bar"}"#);
268 }
269
270 #[test]
271 fn it_converts_from_str() {
272 let document: CosmosDbDocument = r#"{ "foo": "bar" }"#.into();
273 let data = document.as_object().unwrap();
274 assert_eq!(data["foo"].as_str().unwrap(), "bar");
275 }
276
277 #[test]
278 fn it_converts_from_string() {
279 let document: CosmosDbDocument = r#"{ "foo": "bar" }"#.to_string().into();
280 let data = document.as_object().unwrap();
281 assert_eq!(data["foo"].as_str().unwrap(), "bar");
282 }
283
284 #[test]
285 fn it_converts_from_value() {
286 let document: CosmosDbDocument = json!({ "foo": "bar" }).into();
287 let data = document.as_object().unwrap();
288 assert_eq!(data["foo"].as_str().unwrap(), "bar");
289 }
290
291 #[test]
292 fn it_converts_to_string() {
293 let document: CosmosDbDocument = json!({ "foo": "bar" }).into();
294 let string: String = document.into();
295 assert_eq!(string, r#"{"foo":"bar"}"#);
296 }
297
298 #[test]
299 fn it_converts_to_value() {
300 let document: CosmosDbDocument = json!({ "foo": "bar" }).into();
301 let data = document.as_object().unwrap();
302 assert_eq!(data["foo"].as_str().unwrap(), "bar");
303
304 let value: Value = document.into();
305 assert!(value.is_object());
306 assert_eq!(value.as_object().unwrap()["foo"].as_str().unwrap(), "bar");
307 }
308
309 #[test]
310 fn it_converts_to_body() {
311 let document: CosmosDbDocument = r#"{ "foo": "bar" }"#.into();
312 let body: Body = document.into();
313 assert_eq!(body.as_str().unwrap(), r#"{"foo":"bar"}"#);
314
315 let document: CosmosDbDocument = json!({"hello": "world"}).into();
316 let body: Body = document.into();
317 assert_eq!(body.as_str().unwrap(), r#"{"hello":"world"}"#);
318 }
319
320 #[test]
321 fn it_converts_from_typed_data() {
322 let document: CosmosDbDocument = TypedData {
323 data: Some(Data::Json(r#"{ "foo": "bar" }"#.to_string())),
324 }
325 .into();
326
327 let data = document.as_object().unwrap();
328 assert_eq!(data["foo"].as_str().unwrap(), "bar");
329 }
330
331 #[test]
332 fn it_converts_to_typed_data() {
333 let document: CosmosDbDocument = json!({ "foo": "bar" }).into();
334 let data = document.as_object().unwrap();
335 assert_eq!(data["foo"].as_str().unwrap(), "bar");
336
337 let data: TypedData = document.into();
338 assert_eq!(data.data, Some(Data::Json(r#"{"foo":"bar"}"#.to_string())));
339 }
340}