use crate::edb::io::{Reader, Writer};
use crate::edb::{AttributeKind, Constant, Fact};
use crate::error::Error;
use crate::error::Result;
use crate::syntax::{TYPE_NAME_CONST_BOOLEAN, TYPE_NAME_CONST_STRING};
use crate::{error, Collection, Labeled, Relation};
use serde_json::{from_reader, Number, Value};
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Options {
pretty_out: bool,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Json {}
pub const PRAGMA_ID: &str = "json";
impl Options {
pub fn pretty_printed() -> Self {
Self { pretty_out: true }
}
pub fn plain_output() -> Self {
Self { pretty_out: false }
}
}
impl Reader for Json {
type Options = Options;
fn read_from_with_options(
&self,
file_name: &Path,
as_relation: &Relation,
_: &Self::Options,
) -> Result<Relation> {
let file = File::open(file_name)?;
let reader = BufReader::new(file);
let value: Value = from_reader(reader).map_err(|e| Error::Serialization(Box::new(e)))?;
let mut new_relation = as_relation.clone_with_schema_only();
let attribute_types: Vec<AttributeKind> = as_relation
.schema()
.iter()
.map(|a| a.kind().unwrap_or(AttributeKind::String))
.collect();
let arity = attribute_types.len();
let facts = value.as_array().unwrap();
for fact in facts {
let values: &Vec<Value> = fact.as_array().unwrap();
assert_eq!(values.len(), arity);
println!("{:?}", values);
let constants: Result<Vec<Constant>> = values
.iter()
.enumerate()
.map(|(i, v)| match attribute_types.get(i) {
Some(AttributeKind::String) => json_value_to_string(v),
Some(AttributeKind::Integer) => json_value_to_integer(v),
Some(AttributeKind::Boolean) => json_value_to_boolean(v),
_ => unreachable!(),
})
.collect();
new_relation.add(Fact::new(new_relation.label_ref(), constants?)?)?;
}
Ok(new_relation)
}
}
impl Writer for Json {
type Options = Options;
fn write_to_with_options(
&self,
file_name: &Path,
relation: &Relation,
options: &Self::Options,
) -> Result<()> {
let mut file = File::create(file_name)?;
let value: Value = relation_to_json(relation);
if options.pretty_out {
serde_json::to_writer_pretty(&mut file, &value).unwrap();
} else {
serde_json::to_writer(&mut file, &value).unwrap();
}
Ok(())
}
fn print_with_options(&self, relation: &Relation, options: &Self::Options) -> Result<()> {
let value: Value = relation_to_json(relation);
if options.pretty_out {
serde_json::to_writer_pretty(&mut std::io::stdout(), &value).unwrap();
} else {
serde_json::to_writer(&mut std::io::stdout(), &value).unwrap();
}
Ok(())
}
}
fn relation_to_json(relation: &Relation) -> Value {
let mut array: Vec<Value> = Vec::with_capacity(relation.len());
for fact in relation.iter() {
array.push(fact_to_json(fact));
}
Value::Array(array)
}
fn fact_to_json(fact: &Fact) -> Value {
let mut array: Vec<Value> = Vec::with_capacity(fact.len());
for constant in fact.iter() {
array.push(match constant {
Constant::String(v) => Value::String(v.to_owned()),
Constant::Number(v) => {
if let Some(i) = v.as_integer() {
Value::Number(Number::from(*i))
} else if let Some(f) = v.as_float() {
Value::Number(Number::from_f64(*f).unwrap())
} else {
unreachable!()
}
}
Constant::Boolean(v) => Value::Bool(*v),
});
}
Value::Array(array)
}
#[inline]
fn json_value_to_string(v: &Value) -> Result<Constant> {
let v2: String = v
.as_str()
.ok_or_else(|| error::invalid_value(TYPE_NAME_CONST_STRING, &v.to_string()))?
.to_owned();
Ok(Constant::String(v2))
}
#[inline]
fn json_value_to_integer(v: &Value) -> Result<Constant> {
if let Some(v) = v.as_i64() {
Ok(Constant::from(v))
} else if let Some(v) = v.as_u64() {
Ok(Constant::from(v as i64))
} else if let Some(v) = v.as_f64() {
Constant::try_from(v)
} else {
unreachable!()
}
}
#[inline]
fn json_value_to_boolean(v: &Value) -> Result<Constant> {
let v2: bool = v
.as_bool()
.ok_or_else(|| error::invalid_value(TYPE_NAME_CONST_BOOLEAN, &v.to_string()))?
.to_owned();
Ok(Constant::Boolean(v2))
}