2018-12-25 12:26:38 +01:00
|
|
|
use crate::database::update::SerializerError;
|
2018-11-21 13:56:14 +01:00
|
|
|
use std::collections::{HashMap, BTreeMap};
|
2018-12-25 12:26:38 +01:00
|
|
|
use crate::database::calculate_hash;
|
2018-11-20 11:37:19 +01:00
|
|
|
use std::io::{Read, Write};
|
2018-12-28 19:24:50 +01:00
|
|
|
use std::error::Error;
|
2018-12-23 16:46:49 +01:00
|
|
|
use std::{fmt, u16};
|
2018-11-15 17:55:20 +01:00
|
|
|
use std::ops::BitOr;
|
2018-12-15 21:17:55 +01:00
|
|
|
use std::sync::Arc;
|
2018-11-15 17:55:20 +01:00
|
|
|
|
2018-12-03 14:39:56 +01:00
|
|
|
use serde_derive::{Serialize, Deserialize};
|
2018-12-25 12:26:38 +01:00
|
|
|
use serde::ser::{self, Serialize};
|
2018-11-21 13:56:14 +01:00
|
|
|
use linked_hash_map::LinkedHashMap;
|
|
|
|
|
2018-12-25 12:26:38 +01:00
|
|
|
use crate::DocumentId;
|
|
|
|
|
2018-11-15 17:55:20 +01:00
|
|
|
pub const STORED: SchemaProps = SchemaProps { stored: true, indexed: false };
|
|
|
|
pub const INDEXED: SchemaProps = SchemaProps { stored: false, indexed: true };
|
|
|
|
|
2018-11-21 13:56:14 +01:00
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
2018-11-15 17:55:20 +01:00
|
|
|
pub struct SchemaProps {
|
2018-12-28 19:24:50 +01:00
|
|
|
#[serde(default)]
|
2018-11-15 17:55:20 +01:00
|
|
|
stored: bool,
|
2018-12-28 19:24:50 +01:00
|
|
|
|
|
|
|
#[serde(default)]
|
2018-11-15 17:55:20 +01:00
|
|
|
indexed: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SchemaProps {
|
|
|
|
pub fn is_stored(&self) -> bool {
|
|
|
|
self.stored
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_indexed(&self) -> bool {
|
|
|
|
self.indexed
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BitOr for SchemaProps {
|
|
|
|
type Output = Self;
|
|
|
|
|
|
|
|
fn bitor(self, other: Self) -> Self::Output {
|
|
|
|
SchemaProps {
|
|
|
|
stored: self.stored | other.stored,
|
|
|
|
indexed: self.indexed | other.indexed,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-28 19:24:50 +01:00
|
|
|
#[derive(Serialize, Deserialize)]
|
2018-11-20 11:37:19 +01:00
|
|
|
pub struct SchemaBuilder {
|
2018-12-25 12:26:38 +01:00
|
|
|
identifier: String,
|
2018-12-28 19:24:50 +01:00
|
|
|
attributes: LinkedHashMap<String, SchemaProps>,
|
2018-11-20 11:37:19 +01:00
|
|
|
}
|
2018-11-15 17:55:20 +01:00
|
|
|
|
|
|
|
impl SchemaBuilder {
|
2018-12-25 12:26:38 +01:00
|
|
|
pub fn with_identifier<S: Into<String>>(name: S) -> SchemaBuilder {
|
|
|
|
SchemaBuilder {
|
|
|
|
identifier: name.into(),
|
2018-12-28 19:24:50 +01:00
|
|
|
attributes: LinkedHashMap::new(),
|
2018-12-25 12:26:38 +01:00
|
|
|
}
|
2018-11-15 17:55:20 +01:00
|
|
|
}
|
|
|
|
|
2018-11-22 15:44:51 +01:00
|
|
|
pub fn new_attribute<S: Into<String>>(&mut self, name: S, props: SchemaProps) -> SchemaAttr {
|
2018-12-28 19:24:50 +01:00
|
|
|
let len = self.attributes.len();
|
|
|
|
if self.attributes.insert(name.into(), props).is_some() {
|
2018-11-22 15:44:51 +01:00
|
|
|
panic!("Field already inserted.")
|
|
|
|
}
|
2018-12-23 16:46:49 +01:00
|
|
|
SchemaAttr(len as u16)
|
2018-11-15 17:55:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn build(self) -> Schema {
|
2018-11-21 13:56:14 +01:00
|
|
|
let mut attrs = HashMap::new();
|
|
|
|
let mut props = Vec::new();
|
|
|
|
|
2018-12-28 19:24:50 +01:00
|
|
|
for (i, (name, prop)) in self.attributes.into_iter().enumerate() {
|
2018-12-23 16:46:49 +01:00
|
|
|
attrs.insert(name.clone(), SchemaAttr(i as u16));
|
2018-12-15 21:17:55 +01:00
|
|
|
props.push((name, prop));
|
2018-11-21 13:56:14 +01:00
|
|
|
}
|
|
|
|
|
2018-12-25 12:26:38 +01:00
|
|
|
let identifier = self.identifier;
|
|
|
|
Schema { inner: Arc::new(InnerSchema { identifier, attrs, props }) }
|
2018-11-15 17:55:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-21 13:56:14 +01:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
pub struct Schema {
|
2018-12-15 21:17:55 +01:00
|
|
|
inner: Arc<InnerSchema>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
struct InnerSchema {
|
2018-12-25 12:26:38 +01:00
|
|
|
identifier: String,
|
2018-11-21 13:56:14 +01:00
|
|
|
attrs: HashMap<String, SchemaAttr>,
|
2018-12-15 21:17:55 +01:00
|
|
|
props: Vec<(String, SchemaProps)>,
|
2018-11-21 13:56:14 +01:00
|
|
|
}
|
2018-11-15 17:55:20 +01:00
|
|
|
|
|
|
|
impl Schema {
|
2018-12-28 19:24:50 +01:00
|
|
|
pub fn from_toml<R: Read>(mut reader: R) -> Result<Schema, Box<Error>> {
|
|
|
|
let mut buffer = Vec::new();
|
|
|
|
reader.read_to_end(&mut buffer)?;
|
|
|
|
let builder: SchemaBuilder = toml::from_slice(&buffer)?;
|
|
|
|
Ok(builder.build())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_toml<W: Write>(&self, mut writer: W) -> Result<(), Box<Error>> {
|
|
|
|
let identifier = self.inner.identifier.clone();
|
|
|
|
let attributes = self.attributes_ordered();
|
|
|
|
let builder = SchemaBuilder { identifier, attributes };
|
|
|
|
|
|
|
|
let string = toml::to_string_pretty(&builder)?;
|
|
|
|
writer.write_all(string.as_bytes())?;
|
|
|
|
|
|
|
|
Ok(())
|
2018-11-20 11:37:19 +01:00
|
|
|
}
|
|
|
|
|
2018-12-28 19:24:50 +01:00
|
|
|
pub(crate) fn read_from_bin<R: Read>(reader: R) -> bincode::Result<Schema> {
|
|
|
|
let builder: SchemaBuilder = bincode::deserialize_from(reader)?;
|
2018-11-21 13:56:14 +01:00
|
|
|
Ok(builder.build())
|
2018-11-20 11:37:19 +01:00
|
|
|
}
|
|
|
|
|
2018-12-28 19:24:50 +01:00
|
|
|
pub(crate) fn write_to_bin<W: Write>(&self, writer: W) -> bincode::Result<()> {
|
|
|
|
let identifier = self.inner.identifier.clone();
|
|
|
|
let attributes = self.attributes_ordered();
|
|
|
|
let builder = SchemaBuilder { identifier, attributes };
|
|
|
|
|
|
|
|
bincode::serialize_into(writer, &builder)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn attributes_ordered(&self) -> LinkedHashMap<String, SchemaProps> {
|
2018-11-21 13:56:14 +01:00
|
|
|
let mut ordered = BTreeMap::new();
|
2018-12-23 16:46:49 +01:00
|
|
|
for (name, attr) in &self.inner.attrs {
|
|
|
|
let (_, props) = self.inner.props[attr.0 as usize];
|
|
|
|
ordered.insert(attr.0, (name, props));
|
2018-11-21 13:56:14 +01:00
|
|
|
}
|
|
|
|
|
2018-12-28 19:24:50 +01:00
|
|
|
let mut attributes = LinkedHashMap::with_capacity(ordered.len());
|
2018-11-21 13:56:14 +01:00
|
|
|
for (_, (name, props)) in ordered {
|
2018-12-28 19:24:50 +01:00
|
|
|
attributes.insert(name.clone(), props);
|
2018-11-21 13:56:14 +01:00
|
|
|
}
|
|
|
|
|
2018-12-28 19:24:50 +01:00
|
|
|
attributes
|
2018-12-25 12:26:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn document_id<T>(&self, document: &T) -> Result<DocumentId, SerializerError>
|
|
|
|
where T: Serialize,
|
|
|
|
{
|
|
|
|
let find_document_id = FindDocumentIdSerializer {
|
|
|
|
id_attribute_name: self.identifier_name(),
|
|
|
|
};
|
|
|
|
document.serialize(find_document_id)
|
2018-11-15 17:55:20 +01:00
|
|
|
}
|
|
|
|
|
2018-11-21 13:56:14 +01:00
|
|
|
pub fn props(&self, attr: SchemaAttr) -> SchemaProps {
|
2018-12-23 16:46:49 +01:00
|
|
|
let (_, props) = self.inner.props[attr.0 as usize];
|
2018-12-15 21:17:55 +01:00
|
|
|
props
|
2018-11-15 17:55:20 +01:00
|
|
|
}
|
|
|
|
|
2018-12-25 12:26:38 +01:00
|
|
|
pub fn identifier_name(&self) -> &str {
|
|
|
|
&self.inner.identifier
|
|
|
|
}
|
|
|
|
|
2018-11-21 13:56:14 +01:00
|
|
|
pub fn attribute<S: AsRef<str>>(&self, name: S) -> Option<SchemaAttr> {
|
2018-12-15 21:17:55 +01:00
|
|
|
self.inner.attrs.get(name.as_ref()).cloned()
|
2018-11-15 17:55:20 +01:00
|
|
|
}
|
2018-12-02 16:45:17 +01:00
|
|
|
|
|
|
|
pub fn attribute_name(&self, attr: SchemaAttr) -> &str {
|
2018-12-23 16:46:49 +01:00
|
|
|
let (name, _) = &self.inner.props[attr.0 as usize];
|
2018-12-15 21:17:55 +01:00
|
|
|
name
|
2018-12-02 16:45:17 +01:00
|
|
|
}
|
2018-11-15 17:55:20 +01:00
|
|
|
}
|
2018-11-20 11:37:19 +01:00
|
|
|
|
2018-11-21 13:56:14 +01:00
|
|
|
#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
|
2018-12-23 16:46:49 +01:00
|
|
|
pub struct SchemaAttr(pub(crate) u16);
|
2018-11-20 11:37:19 +01:00
|
|
|
|
2018-11-21 13:56:14 +01:00
|
|
|
impl SchemaAttr {
|
2018-12-23 16:46:49 +01:00
|
|
|
pub fn new(value: u16) -> SchemaAttr {
|
2018-12-03 14:39:56 +01:00
|
|
|
SchemaAttr(value)
|
|
|
|
}
|
|
|
|
|
2018-12-07 15:31:58 +01:00
|
|
|
pub fn max() -> SchemaAttr {
|
2018-12-23 16:46:49 +01:00
|
|
|
SchemaAttr(u16::MAX)
|
2018-11-20 11:37:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-21 13:56:14 +01:00
|
|
|
impl fmt::Display for SchemaAttr {
|
2018-11-20 11:37:19 +01:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2018-11-22 15:44:51 +01:00
|
|
|
self.0.fmt(f)
|
2018-11-20 11:37:19 +01:00
|
|
|
}
|
|
|
|
}
|
2018-11-21 13:56:14 +01:00
|
|
|
|
2018-12-25 12:26:38 +01:00
|
|
|
struct FindDocumentIdSerializer<'a> {
|
|
|
|
id_attribute_name: &'a str,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> ser::Serializer for FindDocumentIdSerializer<'a> {
|
|
|
|
type Ok = DocumentId;
|
|
|
|
type Error = SerializerError;
|
|
|
|
type SerializeSeq = ser::Impossible<Self::Ok, Self::Error>;
|
|
|
|
type SerializeTuple = ser::Impossible<Self::Ok, Self::Error>;
|
|
|
|
type SerializeTupleStruct = ser::Impossible<Self::Ok, Self::Error>;
|
|
|
|
type SerializeTupleVariant = ser::Impossible<Self::Ok, Self::Error>;
|
|
|
|
type SerializeMap = ser::Impossible<Self::Ok, Self::Error>;
|
|
|
|
type SerializeStruct = FindDocumentIdStructSerializer<'a>;
|
|
|
|
type SerializeStructVariant = ser::Impossible<Self::Ok, Self::Error>;
|
|
|
|
|
|
|
|
forward_to_unserializable_type! {
|
|
|
|
bool => serialize_bool,
|
|
|
|
char => serialize_char,
|
|
|
|
|
|
|
|
i8 => serialize_i8,
|
|
|
|
i16 => serialize_i16,
|
|
|
|
i32 => serialize_i32,
|
|
|
|
i64 => serialize_i64,
|
|
|
|
|
|
|
|
u8 => serialize_u8,
|
|
|
|
u16 => serialize_u16,
|
|
|
|
u32 => serialize_u32,
|
|
|
|
u64 => serialize_u64,
|
|
|
|
|
|
|
|
f32 => serialize_f32,
|
|
|
|
f64 => serialize_f64,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize_str(self, _v: &str) -> Result<Self::Ok, Self::Error> {
|
|
|
|
Err(SerializerError::UnserializableType { name: "str" })
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize_bytes(self, _v: &[u8]) -> Result<Self::Ok, Self::Error> {
|
|
|
|
Err(SerializerError::UnserializableType { name: "&[u8]" })
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
|
|
|
Err(SerializerError::UnserializableType { name: "Option" })
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize_some<T: ?Sized>(self, _value: &T) -> Result<Self::Ok, Self::Error>
|
|
|
|
where T: Serialize,
|
|
|
|
{
|
|
|
|
Err(SerializerError::UnserializableType { name: "Option" })
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
|
|
|
|
Err(SerializerError::UnserializableType { name: "()" })
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
|
|
|
|
Err(SerializerError::UnserializableType { name: "unit struct" })
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize_unit_variant(
|
|
|
|
self,
|
|
|
|
_name: &'static str,
|
|
|
|
_variant_index: u32,
|
|
|
|
_variant: &'static str
|
|
|
|
) -> Result<Self::Ok, Self::Error>
|
|
|
|
{
|
|
|
|
Err(SerializerError::UnserializableType { name: "unit variant" })
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize_newtype_struct<T: ?Sized>(
|
|
|
|
self,
|
|
|
|
_name: &'static str,
|
|
|
|
value: &T
|
|
|
|
) -> Result<Self::Ok, Self::Error>
|
|
|
|
where T: Serialize,
|
|
|
|
{
|
|
|
|
value.serialize(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize_newtype_variant<T: ?Sized>(
|
|
|
|
self,
|
|
|
|
_name: &'static str,
|
|
|
|
_variant_index: u32,
|
|
|
|
_variant: &'static str,
|
|
|
|
_value: &T
|
|
|
|
) -> Result<Self::Ok, Self::Error>
|
|
|
|
where T: Serialize,
|
|
|
|
{
|
|
|
|
Err(SerializerError::UnserializableType { name: "newtype variant" })
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
|
|
|
Err(SerializerError::UnserializableType { name: "sequence" })
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
|
|
|
|
Err(SerializerError::UnserializableType { name: "tuple" })
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize_tuple_struct(
|
|
|
|
self,
|
|
|
|
_name: &'static str,
|
|
|
|
_len: usize
|
|
|
|
) -> Result<Self::SerializeTupleStruct, Self::Error>
|
|
|
|
{
|
|
|
|
Err(SerializerError::UnserializableType { name: "tuple struct" })
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize_tuple_variant(
|
|
|
|
self,
|
|
|
|
_name: &'static str,
|
|
|
|
_variant_index: u32,
|
|
|
|
_variant: &'static str,
|
|
|
|
_len: usize
|
|
|
|
) -> Result<Self::SerializeTupleVariant, Self::Error>
|
|
|
|
{
|
|
|
|
Err(SerializerError::UnserializableType { name: "tuple variant" })
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
|
|
|
|
// Ok(MapSerializer {
|
|
|
|
// schema: self.schema,
|
|
|
|
// document_id: self.document_id,
|
|
|
|
// new_states: self.new_states,
|
|
|
|
// })
|
|
|
|
Err(SerializerError::UnserializableType { name: "map" })
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize_struct(
|
|
|
|
self,
|
|
|
|
_name: &'static str,
|
|
|
|
_len: usize
|
|
|
|
) -> Result<Self::SerializeStruct, Self::Error>
|
|
|
|
{
|
|
|
|
Ok(FindDocumentIdStructSerializer {
|
|
|
|
id_attribute_name: self.id_attribute_name,
|
|
|
|
document_id: None,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize_struct_variant(
|
|
|
|
self,
|
|
|
|
_name: &'static str,
|
|
|
|
_variant_index: u32,
|
|
|
|
_variant: &'static str,
|
|
|
|
_len: usize
|
|
|
|
) -> Result<Self::SerializeStructVariant, Self::Error>
|
|
|
|
{
|
|
|
|
Err(SerializerError::UnserializableType { name: "struct variant" })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct FindDocumentIdStructSerializer<'a> {
|
|
|
|
id_attribute_name: &'a str,
|
|
|
|
document_id: Option<DocumentId>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> ser::SerializeStruct for FindDocumentIdStructSerializer<'a> {
|
|
|
|
type Ok = DocumentId;
|
|
|
|
type Error = SerializerError;
|
|
|
|
|
|
|
|
fn serialize_field<T: ?Sized>(
|
|
|
|
&mut self,
|
|
|
|
key: &'static str,
|
|
|
|
value: &T
|
|
|
|
) -> Result<(), Self::Error>
|
|
|
|
where T: Serialize,
|
|
|
|
{
|
|
|
|
if self.id_attribute_name == key {
|
|
|
|
// TODO can it be possible to have multiple ids?
|
|
|
|
let id = bincode::serialize(value).unwrap();
|
|
|
|
let hash = calculate_hash(&id);
|
|
|
|
self.document_id = Some(DocumentId(hash));
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn end(self) -> Result<Self::Ok, Self::Error> {
|
|
|
|
match self.document_id {
|
|
|
|
Some(document_id) => Ok(document_id),
|
|
|
|
None => Err(SerializerError::DocumentIdNotFound)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-21 13:56:14 +01:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2018-12-28 19:24:50 +01:00
|
|
|
use std::error::Error;
|
2018-11-21 13:56:14 +01:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn serialize_deserialize() -> bincode::Result<()> {
|
2018-12-25 12:26:38 +01:00
|
|
|
let mut builder = SchemaBuilder::with_identifier("id");
|
2018-12-28 19:24:50 +01:00
|
|
|
builder.new_attribute("alpha", STORED);
|
2018-11-22 15:44:51 +01:00
|
|
|
builder.new_attribute("beta", STORED | INDEXED);
|
|
|
|
builder.new_attribute("gamma", INDEXED);
|
2018-11-21 13:56:14 +01:00
|
|
|
let schema = builder.build();
|
|
|
|
|
|
|
|
let mut buffer = Vec::new();
|
|
|
|
|
2018-12-28 19:24:50 +01:00
|
|
|
schema.write_to_bin(&mut buffer)?;
|
|
|
|
let schema2 = Schema::read_from_bin(buffer.as_slice())?;
|
|
|
|
|
|
|
|
assert_eq!(schema, schema2);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn serialize_deserialize_toml() -> Result<(), Box<Error>> {
|
|
|
|
let mut builder = SchemaBuilder::with_identifier("id");
|
|
|
|
builder.new_attribute("alpha", STORED);
|
|
|
|
builder.new_attribute("beta", STORED | INDEXED);
|
|
|
|
builder.new_attribute("gamma", INDEXED);
|
|
|
|
let schema = builder.build();
|
|
|
|
|
|
|
|
let mut buffer = Vec::new();
|
|
|
|
schema.to_toml(&mut buffer)?;
|
|
|
|
|
|
|
|
let schema2 = Schema::from_toml(buffer.as_slice())?;
|
|
|
|
assert_eq!(schema, schema2);
|
|
|
|
|
|
|
|
let data = r#"
|
|
|
|
identifier = "id"
|
|
|
|
|
|
|
|
[attributes."alpha"]
|
|
|
|
stored = true
|
|
|
|
|
|
|
|
[attributes."beta"]
|
|
|
|
stored = true
|
|
|
|
indexed = true
|
2018-11-21 13:56:14 +01:00
|
|
|
|
2018-12-28 19:24:50 +01:00
|
|
|
[attributes."gamma"]
|
|
|
|
indexed = true
|
|
|
|
"#;
|
|
|
|
let schema2 = Schema::from_toml(data.as_bytes())?;
|
2018-11-21 13:56:14 +01:00
|
|
|
assert_eq!(schema, schema2);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|