2018-11-21 13:56:14 +01:00
|
|
|
use std::collections::{HashMap, BTreeMap};
|
2018-11-20 11:37:19 +01:00
|
|
|
use std::io::{Read, Write};
|
2018-11-15 17:55:20 +01:00
|
|
|
use std::path::Path;
|
|
|
|
use std::ops::BitOr;
|
2018-11-20 11:37:19 +01:00
|
|
|
use std::fs::File;
|
2018-11-15 17:55:20 +01:00
|
|
|
use std::fmt;
|
|
|
|
|
2018-12-03 14:39:56 +01:00
|
|
|
use serde_derive::{Serialize, Deserialize};
|
2018-11-21 13:56:14 +01:00
|
|
|
use linked_hash_map::LinkedHashMap;
|
|
|
|
|
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 {
|
|
|
|
stored: bool,
|
|
|
|
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-11-20 11:37:19 +01:00
|
|
|
pub struct SchemaBuilder {
|
2018-11-21 13:56:14 +01:00
|
|
|
attrs: LinkedHashMap<String, SchemaProps>,
|
2018-11-20 11:37:19 +01:00
|
|
|
}
|
2018-11-15 17:55:20 +01:00
|
|
|
|
|
|
|
impl SchemaBuilder {
|
|
|
|
pub fn new() -> SchemaBuilder {
|
2018-11-21 13:56:14 +01:00
|
|
|
SchemaBuilder { attrs: LinkedHashMap::new() }
|
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-11-21 13:56:14 +01:00
|
|
|
let len = self.attrs.len();
|
2018-11-22 15:44:51 +01:00
|
|
|
if self.attrs.insert(name.into(), props).is_some() {
|
|
|
|
panic!("Field already inserted.")
|
|
|
|
}
|
2018-11-21 13:56:14 +01:00
|
|
|
SchemaAttr(len as u32)
|
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();
|
|
|
|
|
|
|
|
for (i, (name, prop)) in self.attrs.into_iter().enumerate() {
|
|
|
|
attrs.insert(name, SchemaAttr(i as u32));
|
|
|
|
props.push(prop);
|
|
|
|
}
|
|
|
|
|
|
|
|
Schema { 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 {
|
|
|
|
attrs: HashMap<String, SchemaAttr>,
|
|
|
|
props: Vec<SchemaProps>,
|
|
|
|
}
|
2018-11-15 17:55:20 +01:00
|
|
|
|
|
|
|
impl Schema {
|
2018-11-21 13:56:14 +01:00
|
|
|
pub fn open<P: AsRef<Path>>(path: P) -> bincode::Result<Schema> {
|
2018-11-20 11:37:19 +01:00
|
|
|
let file = File::open(path)?;
|
|
|
|
Schema::read_from(file)
|
|
|
|
}
|
|
|
|
|
2018-11-21 13:56:14 +01:00
|
|
|
pub fn read_from<R: Read>(reader: R) -> bincode::Result<Schema> {
|
|
|
|
let attrs = bincode::deserialize_from(reader)?;
|
|
|
|
let builder = SchemaBuilder { attrs };
|
|
|
|
Ok(builder.build())
|
2018-11-20 11:37:19 +01:00
|
|
|
}
|
|
|
|
|
2018-11-21 13:56:14 +01:00
|
|
|
pub fn write_to<W: Write>(&self, writer: W) -> bincode::Result<()> {
|
|
|
|
let mut ordered = BTreeMap::new();
|
|
|
|
for (name, field) in &self.attrs {
|
|
|
|
let index = field.as_u32();
|
|
|
|
let props = self.props[index as usize];
|
|
|
|
ordered.insert(index, (name, props));
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut attrs = LinkedHashMap::with_capacity(ordered.len());
|
|
|
|
for (_, (name, props)) in ordered {
|
|
|
|
attrs.insert(name, props);
|
|
|
|
}
|
|
|
|
|
|
|
|
bincode::serialize_into(writer, &attrs)
|
2018-11-15 17:55:20 +01:00
|
|
|
}
|
|
|
|
|
2018-11-21 13:56:14 +01:00
|
|
|
pub fn props(&self, attr: SchemaAttr) -> SchemaProps {
|
|
|
|
self.props[attr.as_u32() as usize]
|
2018-11-15 17:55:20 +01:00
|
|
|
}
|
|
|
|
|
2018-11-21 13:56:14 +01:00
|
|
|
pub fn attribute<S: AsRef<str>>(&self, name: S) -> Option<SchemaAttr> {
|
|
|
|
self.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-03 22:26:24 +01:00
|
|
|
// FIXME complexity is insane !
|
|
|
|
for (key, &value) in &self.attrs {
|
|
|
|
if value == attr { return &key }
|
|
|
|
}
|
|
|
|
panic!("schema attribute name not found for {:?}", attr)
|
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)]
|
|
|
|
pub struct SchemaAttr(u32);
|
2018-11-20 11:37:19 +01:00
|
|
|
|
2018-11-21 13:56:14 +01:00
|
|
|
impl SchemaAttr {
|
2018-12-03 14:39:56 +01:00
|
|
|
pub fn new(value: u32) -> SchemaAttr {
|
|
|
|
SchemaAttr(value)
|
|
|
|
}
|
|
|
|
|
2018-11-20 11:37:19 +01:00
|
|
|
pub fn as_u32(&self) -> u32 {
|
|
|
|
self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn serialize_deserialize() -> bincode::Result<()> {
|
|
|
|
let mut builder = SchemaBuilder::new();
|
2018-11-22 15:44:51 +01:00
|
|
|
builder.new_attribute("alphabet", STORED);
|
|
|
|
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();
|
|
|
|
|
|
|
|
schema.write_to(&mut buffer)?;
|
|
|
|
let schema2 = Schema::read_from(buffer.as_slice())?;
|
|
|
|
|
|
|
|
assert_eq!(schema, schema2);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|