azure_functions/bindings/
table.rs1use crate::{
2 http::Body,
3 rpc::{typed_data::Data, TypedData},
4};
5use serde_json::{from_str, json, Map, Value};
6use std::fmt;
7
8#[derive(Default, Debug, Clone)]
53pub struct Table(Value);
54
55pub type Row = Map<String, Value>;
57
58impl Table {
59 pub fn new() -> Table {
63 Table(Value::Array(Vec::new()))
64 }
65
66 pub fn is_empty(&self) -> bool {
68 self.0.as_array().unwrap().is_empty()
69 }
70
71 pub fn len(&self) -> usize {
73 self.0.as_array().unwrap().len()
74 }
75
76 pub fn rows(&self) -> impl Iterator<Item = &Row> {
83 self.0
84 .as_array()
85 .unwrap()
86 .iter()
87 .map(|x| x.as_object().unwrap())
88 }
89
90 pub fn add_row(&mut self, partition_key: &str, row_key: &str) -> &mut Row {
92 let array = self.0.as_array_mut().unwrap();
93
94 array.push(json!({
95 "PartitionKey": partition_key,
96 "RowKey": row_key
97 }));
98
99 array.last_mut().unwrap().as_object_mut().unwrap()
100 }
101
102 pub fn add_row_value(&mut self, value: Value) {
104 let array = self.0.as_array_mut().unwrap();
105
106 array.push(value);
107 }
108
109 pub fn as_value(&self) -> &Value {
111 &self.0
112 }
113}
114
115impl fmt::Display for Table {
116 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
117 write!(f, "{}", self.0)
118 }
119}
120
121#[doc(hidden)]
122impl From<TypedData> for Table {
123 fn from(data: TypedData) -> Self {
124 match &data.data {
125 Some(Data::Json(s)) => {
126 let mut rows: Value =
127 from_str(s).expect("expected valid JSON data for table binding");
128
129 if rows.is_object() {
130 rows = Value::Array(vec![rows]);
131 }
132
133 if !rows.is_array() {
134 panic!("expected an object or array for table binding data");
135 }
136
137 Table(rows)
138 }
139 _ => Table::new(),
140 }
141 }
142}
143
144impl Into<Value> for Table {
145 fn into(self) -> Value {
146 self.0
147 }
148}
149
150impl<'a> Into<Body<'a>> for Table {
151 fn into(self) -> Body<'a> {
152 self.0.into()
153 }
154}
155
156#[doc(hidden)]
157impl Into<TypedData> for Table {
158 fn into(self) -> TypedData {
159 TypedData {
160 data: Some(Data::Json(self.0.to_string())),
161 }
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use std::fmt::Write;
169
170 #[test]
171 fn it_constructs_an_empty_table() {
172 let table = Table::new();
173 assert_eq!(table.len(), 0);
174 assert_eq!(table.rows().count(), 0);
175 assert!(table.is_empty());
176 }
177
178 #[test]
179 fn it_is_not_empty_when_rows_are_present() {
180 let mut table = Table::new();
181 table.add_row("partition1", "row1");
182 assert!(!table.is_empty());
183 }
184
185 #[test]
186 fn it_has_a_length_equal_to_number_of_rows() {
187 let mut table = Table::new();
188 assert_eq!(table.len(), 0);
189 table.add_row("partition1", "row1");
190 table.add_row("partition2", "row2");
191 table.add_row("partition3", "row3");
192 assert_eq!(table.len(), 3);
193 }
194
195 #[test]
196 fn it_iterates_rows() {
197 let mut table = Table::new();
198 assert_eq!(table.len(), 0);
199 table.add_row("partition1", "row1");
200 table.add_row("partition2", "row2");
201 table.add_row("partition3", "row3");
202 assert_eq!(table.len(), 3);
203
204 for (i, row) in table.rows().enumerate() {
205 assert_eq!(
206 row.get("PartitionKey").unwrap().as_str().unwrap(),
207 format!("partition{}", i + 1)
208 );
209 assert_eq!(
210 row.get("RowKey").unwrap().as_str().unwrap(),
211 format!("row{}", i + 1)
212 );
213 }
214 }
215
216 #[test]
217 fn it_adds_row_value() {
218 let mut table = Table::new();
219 assert_eq!(table.len(), 0);
220 table.add_row_value(json!({
221 "PartitionKey": "partition1",
222 "RowKey": "row1",
223 "data": "hello world"
224 }));
225
226 assert_eq!(
227 table.to_string(),
228 r#"[{"PartitionKey":"partition1","RowKey":"row1","data":"hello world"}]"#
229 );
230 }
231
232 #[test]
233 fn it_casts_to_value_reference() {
234 let mut table = Table::new();
235 table.add_row("partition1", "row1");
236
237 assert_eq!(
238 table.as_value().to_string(),
239 r#"[{"PartitionKey":"partition1","RowKey":"row1"}]"#
240 );
241 }
242
243 #[test]
244 fn it_displays_as_a_string() {
245 let mut table = Table::new();
246 {
247 let row = table.add_row("partition1", "row1");
248 row.insert("data".to_string(), Value::String("value".to_string()));
249 }
250 let mut s = String::new();
251 write!(s, "{}", table).unwrap();
252
253 assert_eq!(
254 s,
255 r#"[{"PartitionKey":"partition1","RowKey":"row1","data":"value"}]"#
256 );
257 }
258
259 #[test]
260 fn it_converts_from_typed_data() {
261 const TABLE: &'static str =
262 r#"[{"PartitionKey":"partition1","RowKey":"row1","data":"value"}]"#;
263
264 let data = TypedData {
265 data: Some(Data::Json(TABLE.to_string())),
266 };
267
268 let table: Table = data.into();
269 assert_eq!(table.len(), 1);
270 assert_eq!(table.to_string(), TABLE);
271
272 let data = TypedData {
273 data: Some(Data::String("".to_string())),
274 };
275
276 let table: Table = data.into();
277 assert_eq!(table.len(), 0);
278 assert!(table.is_empty());
279 }
280
281 #[test]
282 fn it_converts_to_json() {
283 let mut table = Table::new();
284 table.add_row("partition1", "row1");
285
286 let value: Value = table.into();
287
288 assert_eq!(
289 value.to_string(),
290 r#"[{"PartitionKey":"partition1","RowKey":"row1"}]"#
291 );
292 }
293
294 #[test]
295 fn it_converts_to_body() {
296 const TABLE: &'static str =
297 r#"[{"PartitionKey":"partition1","RowKey":"row1","data":"value"}]"#;
298
299 let data = TypedData {
300 data: Some(Data::Json(TABLE.to_string())),
301 };
302
303 let table: Table = data.into();
304 let body: Body = table.into();
305 assert_eq!(
306 body.as_str().unwrap(),
307 r#"[{"PartitionKey":"partition1","RowKey":"row1","data":"value"}]"#
308 );
309 }
310
311 #[test]
312 fn it_converts_to_typed_data() {
313 let mut table = Table::new();
314 {
315 let row = table.add_row("partition1", "row1");
316 row.insert("data".to_string(), Value::String("value".to_string()));
317 }
318 let data: TypedData = table.into();
319 assert_eq!(
320 data.data,
321 Some(Data::Json(
322 r#"[{"PartitionKey":"partition1","RowKey":"row1","data":"value"}]"#.to_string()
323 ))
324 );
325 }
326}