use crate::{
http::Body,
rpc::{typed_data::Data, TypedData},
};
use serde_json::{from_str, json, Map, Value};
use std::fmt;
#[derive(Default, Debug, Clone)]
pub struct Table(Value);
pub type Row = Map<String, Value>;
impl Table {
pub fn new() -> Table {
Table(Value::Array(Vec::new()))
}
pub fn is_empty(&self) -> bool {
self.0.as_array().unwrap().is_empty()
}
pub fn len(&self) -> usize {
self.0.as_array().unwrap().len()
}
pub fn rows(&self) -> impl Iterator<Item = &Row> {
self.0
.as_array()
.unwrap()
.iter()
.map(|x| x.as_object().unwrap())
}
pub fn add_row(&mut self, partition_key: &str, row_key: &str) -> &mut Row {
let array = self.0.as_array_mut().unwrap();
array.push(json!({
"PartitionKey": partition_key,
"RowKey": row_key
}));
array.last_mut().unwrap().as_object_mut().unwrap()
}
pub fn add_row_value(&mut self, value: Value) {
let array = self.0.as_array_mut().unwrap();
array.push(value);
}
pub fn as_value(&self) -> &Value {
&self.0
}
}
impl fmt::Display for Table {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[doc(hidden)]
impl From<TypedData> for Table {
fn from(data: TypedData) -> Self {
match &data.data {
Some(Data::Json(s)) => {
let mut rows: Value =
from_str(s).expect("expected valid JSON data for table binding");
if rows.is_object() {
rows = Value::Array(vec![rows]);
}
if !rows.is_array() {
panic!("expected an object or array for table binding data");
}
Table(rows)
}
_ => Table::new(),
}
}
}
impl Into<Value> for Table {
fn into(self) -> Value {
self.0
}
}
impl<'a> Into<Body<'a>> for Table {
fn into(self) -> Body<'a> {
self.0.into()
}
}
#[doc(hidden)]
impl Into<TypedData> for Table {
fn into(self) -> TypedData {
TypedData {
data: Some(Data::Json(self.0.to_string())),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fmt::Write;
#[test]
fn it_constructs_an_empty_table() {
let table = Table::new();
assert_eq!(table.len(), 0);
assert_eq!(table.rows().count(), 0);
assert!(table.is_empty());
}
#[test]
fn it_is_not_empty_when_rows_are_present() {
let mut table = Table::new();
table.add_row("partition1", "row1");
assert!(!table.is_empty());
}
#[test]
fn it_has_a_length_equal_to_number_of_rows() {
let mut table = Table::new();
assert_eq!(table.len(), 0);
table.add_row("partition1", "row1");
table.add_row("partition2", "row2");
table.add_row("partition3", "row3");
assert_eq!(table.len(), 3);
}
#[test]
fn it_iterates_rows() {
let mut table = Table::new();
assert_eq!(table.len(), 0);
table.add_row("partition1", "row1");
table.add_row("partition2", "row2");
table.add_row("partition3", "row3");
assert_eq!(table.len(), 3);
for (i, row) in table.rows().enumerate() {
assert_eq!(
row.get("PartitionKey").unwrap().as_str().unwrap(),
format!("partition{}", i + 1)
);
assert_eq!(
row.get("RowKey").unwrap().as_str().unwrap(),
format!("row{}", i + 1)
);
}
}
#[test]
fn it_adds_row_value() {
let mut table = Table::new();
assert_eq!(table.len(), 0);
table.add_row_value(json!({
"PartitionKey": "partition1",
"RowKey": "row1",
"data": "hello world"
}));
assert_eq!(
table.to_string(),
r#"[{"PartitionKey":"partition1","RowKey":"row1","data":"hello world"}]"#
);
}
#[test]
fn it_casts_to_value_reference() {
let mut table = Table::new();
table.add_row("partition1", "row1");
assert_eq!(
table.as_value().to_string(),
r#"[{"PartitionKey":"partition1","RowKey":"row1"}]"#
);
}
#[test]
fn it_displays_as_a_string() {
let mut table = Table::new();
{
let row = table.add_row("partition1", "row1");
row.insert("data".to_string(), Value::String("value".to_string()));
}
let mut s = String::new();
write!(s, "{}", table).unwrap();
assert_eq!(
s,
r#"[{"PartitionKey":"partition1","RowKey":"row1","data":"value"}]"#
);
}
#[test]
fn it_converts_from_typed_data() {
const TABLE: &'static str =
r#"[{"PartitionKey":"partition1","RowKey":"row1","data":"value"}]"#;
let data = TypedData {
data: Some(Data::Json(TABLE.to_string())),
};
let table: Table = data.into();
assert_eq!(table.len(), 1);
assert_eq!(table.to_string(), TABLE);
let data = TypedData {
data: Some(Data::String("".to_string())),
};
let table: Table = data.into();
assert_eq!(table.len(), 0);
assert!(table.is_empty());
}
#[test]
fn it_converts_to_json() {
let mut table = Table::new();
table.add_row("partition1", "row1");
let value: Value = table.into();
assert_eq!(
value.to_string(),
r#"[{"PartitionKey":"partition1","RowKey":"row1"}]"#
);
}
#[test]
fn it_converts_to_body() {
const TABLE: &'static str =
r#"[{"PartitionKey":"partition1","RowKey":"row1","data":"value"}]"#;
let data = TypedData {
data: Some(Data::Json(TABLE.to_string())),
};
let table: Table = data.into();
let body: Body = table.into();
assert_eq!(
body.as_str().unwrap(),
r#"[{"PartitionKey":"partition1","RowKey":"row1","data":"value"}]"#
);
}
#[test]
fn it_converts_to_typed_data() {
let mut table = Table::new();
{
let row = table.add_row("partition1", "row1");
row.insert("data".to_string(), Value::String("value".to_string()));
}
let data: TypedData = table.into();
assert_eq!(
data.data,
Some(Data::Json(
r#"[{"PartitionKey":"partition1","RowKey":"row1","data":"value"}]"#.to_string()
))
);
}
}