2026-01-31 13:03:01 -05:00
|
|
|
use crate::{
|
2026-02-08 12:57:29 -05:00
|
|
|
action::{CalcValue, MsgAction, Operand, Query},
|
2026-02-03 08:56:42 -05:00
|
|
|
document::field::{Field, FieldType},
|
2026-02-12 22:49:19 -05:00
|
|
|
mtterror::{ErrorID, MTTError},
|
2026-01-31 13:03:01 -05:00
|
|
|
name::{NameType, Names},
|
2026-02-03 08:56:42 -05:00
|
|
|
queue::data_director::{Include, Path, Route},
|
2026-01-31 13:03:01 -05:00
|
|
|
};
|
|
|
|
|
use chrono::prelude::*;
|
|
|
|
|
use std::{
|
|
|
|
|
collections::{HashMap, HashSet},
|
|
|
|
|
time::Duration,
|
|
|
|
|
};
|
|
|
|
|
use uuid::Uuid;
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub struct Message {
|
|
|
|
|
msg_id: Uuid,
|
|
|
|
|
document_id: NameType,
|
|
|
|
|
action: MsgAction,
|
|
|
|
|
route: Route,
|
|
|
|
|
// session: Option<?>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Message {
|
|
|
|
|
pub fn new<D, A>(doc_id: D, action: A) -> Self
|
|
|
|
|
where
|
|
|
|
|
D: Into<NameType>,
|
|
|
|
|
A: Into<MsgAction>,
|
|
|
|
|
{
|
|
|
|
|
Self {
|
|
|
|
|
msg_id: Uuid::new_v4(),
|
|
|
|
|
document_id: doc_id.into(),
|
|
|
|
|
action: action.into(),
|
|
|
|
|
route: Route::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_message_id(&self) -> &Uuid {
|
|
|
|
|
&self.msg_id
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_action(&self) -> &MsgAction {
|
|
|
|
|
&self.action
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_path(&self) -> Path {
|
|
|
|
|
Path::new(
|
|
|
|
|
Include::Just(self.msg_id.clone()),
|
|
|
|
|
Include::Just(self.document_id.clone()),
|
|
|
|
|
Include::Just(self.action.clone().into()),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_route(&self) -> Route {
|
|
|
|
|
self.route.clone()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_route(&mut self, route: Route) {
|
|
|
|
|
self.route = route;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn response<A>(&self, action: A) -> Self
|
|
|
|
|
where
|
|
|
|
|
A: Into<MsgAction>,
|
|
|
|
|
{
|
|
|
|
|
Self {
|
|
|
|
|
msg_id: self.msg_id.clone(),
|
|
|
|
|
document_id: self.document_id.clone(),
|
|
|
|
|
action: action.into(),
|
|
|
|
|
route: Route::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn forward<D, A>(&self, doc_id: D, action: A) -> Self
|
|
|
|
|
where
|
|
|
|
|
D: Into<NameType>,
|
|
|
|
|
A: Into<MsgAction>,
|
|
|
|
|
{
|
|
|
|
|
Self {
|
|
|
|
|
msg_id: self.msg_id.clone(),
|
|
|
|
|
document_id: doc_id.into(),
|
|
|
|
|
action: action.into(),
|
|
|
|
|
route: Route::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod messages {
|
|
|
|
|
use super::*;
|
2026-02-03 08:56:42 -05:00
|
|
|
use crate::{document::definition::DocDef, name::Name};
|
2026-01-31 13:03:01 -05:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn can_the_document_be_a_named_reference() {
|
|
|
|
|
let dts = [Name::english("one"), Name::english("two")];
|
|
|
|
|
for document in dts.into_iter() {
|
|
|
|
|
let msg = Message::new(
|
|
|
|
|
document.clone(),
|
|
|
|
|
MsgAction::Create(DocDef::new(document.clone())),
|
|
|
|
|
);
|
|
|
|
|
match &msg.document_id {
|
|
|
|
|
NameType::Name(data) => assert_eq!(data, &document),
|
|
|
|
|
_ => unreachable!("should have been a string id"),
|
|
|
|
|
}
|
|
|
|
|
match msg.get_action() {
|
|
|
|
|
MsgAction::Create(_) => {}
|
|
|
|
|
_ => unreachable!("should have been a create document"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn can_the_document_be_an_id() {
|
|
|
|
|
let document = Uuid::new_v4();
|
2026-02-09 19:28:22 -05:00
|
|
|
let msg = Message::new(document.clone(), Query::new(document.clone()));
|
2026-01-31 13:03:01 -05:00
|
|
|
match msg.document_id {
|
|
|
|
|
NameType::ID(data) => assert_eq!(data, document),
|
|
|
|
|
_ => unreachable!("should have been an id"),
|
|
|
|
|
}
|
|
|
|
|
match msg.get_action() {
|
|
|
|
|
MsgAction::Query(_) => {}
|
|
|
|
|
_ => unreachable!("should have been an access query"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn do_messages_contain_routes() {
|
2026-02-09 19:28:22 -05:00
|
|
|
let name = Name::english("whatever");
|
|
|
|
|
let mut msg = Message::new(name.clone(), Query::new(name.clone()));
|
2026-01-31 13:03:01 -05:00
|
|
|
let default_route = msg.get_route();
|
|
|
|
|
match default_route.msg_id {
|
|
|
|
|
Include::Just(_) => unreachable!("should defalt to all"),
|
|
|
|
|
Include::All => {}
|
|
|
|
|
}
|
|
|
|
|
match default_route.doc_id {
|
|
|
|
|
Include::Just(_) => unreachable!("should defalt to all"),
|
|
|
|
|
Include::All => {}
|
|
|
|
|
}
|
|
|
|
|
match default_route.action {
|
|
|
|
|
Include::Just(_) => unreachable!("should defalt to all"),
|
|
|
|
|
Include::All => {}
|
|
|
|
|
}
|
|
|
|
|
let doc_id = Uuid::new_v4();
|
|
|
|
|
let route = Route::new(
|
|
|
|
|
Include::Just(msg.get_message_id().clone()),
|
|
|
|
|
Include::Just(doc_id.clone()),
|
|
|
|
|
Include::Just(msg.get_action().into()),
|
|
|
|
|
);
|
|
|
|
|
msg.set_route(route);
|
|
|
|
|
let result = msg.get_route();
|
|
|
|
|
match result.msg_id {
|
|
|
|
|
Include::Just(data) => assert_eq!(&data, msg.get_message_id()),
|
|
|
|
|
Include::All => unreachable!("should have message id"),
|
|
|
|
|
}
|
|
|
|
|
match result.doc_id {
|
|
|
|
|
Include::Just(data) => assert_eq!(data, doc_id),
|
|
|
|
|
Include::All => unreachable!("should have document id"),
|
|
|
|
|
}
|
|
|
|
|
match result.action {
|
|
|
|
|
Include::Just(data) => assert_eq!(data, msg.get_action().into()),
|
|
|
|
|
Include::All => unreachable!("should have action"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn is_the_message_id_random() {
|
|
|
|
|
let mut ids: Vec<Uuid> = Vec::new();
|
|
|
|
|
for _ in 0..5 {
|
2026-02-09 19:28:22 -05:00
|
|
|
let msg = Message::new(Name::english("tester"), Query::new(Name::english("test")));
|
2026-01-31 13:03:01 -05:00
|
|
|
let id = msg.get_message_id().clone();
|
|
|
|
|
assert!(!ids.contains(&id), "{:?} containts {}", ids, id);
|
|
|
|
|
ids.push(id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn can_make_reply_message() {
|
|
|
|
|
let name = Name::english("testing");
|
2026-02-09 19:28:22 -05:00
|
|
|
let msg = Message::new(name.clone(), Query::new(name.clone()));
|
2026-01-31 13:03:01 -05:00
|
|
|
let responce = Reply::new();
|
|
|
|
|
let reply = msg.response(responce);
|
|
|
|
|
assert_eq!(reply.get_message_id(), msg.get_message_id());
|
|
|
|
|
match &reply.document_id {
|
|
|
|
|
NameType::Name(data) => assert_eq!(data, &name),
|
|
|
|
|
_ => unreachable!("should have been a name"),
|
|
|
|
|
}
|
|
|
|
|
match reply.get_action() {
|
|
|
|
|
MsgAction::Reply(_) => {}
|
|
|
|
|
_ => unreachable!("should have been a reply"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn can_make_error_message() {
|
|
|
|
|
let name = Name::english("testing");
|
2026-02-09 19:28:22 -05:00
|
|
|
let msg = Message::new(name.clone(), Query::new(name.clone()));
|
2026-01-31 13:03:01 -05:00
|
|
|
let err_msg = Uuid::new_v4().to_string();
|
2026-02-12 22:49:19 -05:00
|
|
|
let result = msg.response(MTTError::new(NameType::None, ErrorID::DocumentNotFound));
|
2026-01-31 13:03:01 -05:00
|
|
|
assert_eq!(result.get_message_id(), msg.get_message_id());
|
|
|
|
|
match &result.document_id {
|
|
|
|
|
NameType::Name(data) => assert_eq!(data, &name),
|
|
|
|
|
_ => unreachable!("should have been a name"),
|
|
|
|
|
}
|
|
|
|
|
match result.get_action() {
|
2026-02-12 22:49:19 -05:00
|
|
|
MsgAction::Error(data) => match data.error_id() {
|
|
|
|
|
ErrorID::DocumentNotFound => {}
|
2026-01-31 13:03:01 -05:00
|
|
|
_ => unreachable!("got {:?}, should have received not found", data),
|
|
|
|
|
},
|
|
|
|
|
_ => unreachable!("should have been a reply"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn can_make_a_response_message() {
|
|
|
|
|
let doc_id = Uuid::new_v4();
|
2026-02-09 19:28:22 -05:00
|
|
|
let msg = Message::new(doc_id.clone(), Query::new(doc_id.clone()));
|
2026-01-31 13:03:01 -05:00
|
|
|
let data = Uuid::new_v4().to_string();
|
2026-02-12 22:49:19 -05:00
|
|
|
let result1 = msg.response(MTTError::new(NameType::None, ErrorID::DocumentNotFound));
|
2026-01-31 13:03:01 -05:00
|
|
|
let result2 = msg.response(Reply::new());
|
|
|
|
|
assert_eq!(result1.get_message_id(), msg.get_message_id());
|
|
|
|
|
assert_eq!(result2.get_message_id(), msg.get_message_id());
|
|
|
|
|
assert_eq!(result1.document_id, msg.document_id);
|
|
|
|
|
assert_eq!(result2.document_id, msg.document_id);
|
|
|
|
|
let action1 = result1.get_action();
|
|
|
|
|
match action1 {
|
2026-02-12 22:49:19 -05:00
|
|
|
MsgAction::Error(err) => match err.error_id() {
|
|
|
|
|
ErrorID::DocumentNotFound => {}
|
2026-01-31 13:03:01 -05:00
|
|
|
_ => unreachable!("got {:?}: should have received document not found", err),
|
|
|
|
|
},
|
|
|
|
|
_ => unreachable!("got {:?}: should have received error", action1),
|
|
|
|
|
}
|
|
|
|
|
let action2 = result2.get_action();
|
|
|
|
|
match action2 {
|
|
|
|
|
MsgAction::Reply(data) => assert_eq!(data.len(), 0),
|
|
|
|
|
_ => unreachable!("got {:?}: should have received a reply", action2),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub struct Operation {
|
|
|
|
|
field_name: String,
|
|
|
|
|
operation: Operand,
|
|
|
|
|
value: Field,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
impl Operation {
|
|
|
|
|
pub fn new<F>(name: String, op: Operand, value: F) -> Self
|
|
|
|
|
where
|
|
|
|
|
F: Into<Field>,
|
|
|
|
|
{
|
|
|
|
|
Self {
|
|
|
|
|
field_name: name,
|
|
|
|
|
operation: op,
|
|
|
|
|
value: value.into(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn which_field(&self) -> String {
|
|
|
|
|
self.field_name.clone()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn validate(&self, field: &Field) -> bool {
|
|
|
|
|
self.operation.validate(field, &self.value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
enum QueryType {
|
|
|
|
|
Query(Query),
|
|
|
|
|
Oids(HashSet<Oid>),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<Query> for QueryType {
|
|
|
|
|
fn from(value: Query) -> Self {
|
|
|
|
|
QueryType::Query(value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<HashSet<Oid>> for QueryType {
|
|
|
|
|
fn from(value: HashSet<Oid>) -> Self {
|
|
|
|
|
QueryType::Oids(value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub struct Reply {
|
|
|
|
|
data: Vec<Document>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
impl Reply {
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
Self { data: Vec::new() }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn add(&mut self, doc: Document) {
|
|
|
|
|
self.data.push(doc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
|
self.data.len()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn iter(&self) -> impl Iterator<Item = &Document> {
|
|
|
|
|
self.data.iter()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod replies {
|
|
|
|
|
use super::*;
|
|
|
|
|
use crate::name::Name;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn is_new_empty() {
|
|
|
|
|
let reply = Reply::new();
|
|
|
|
|
assert_eq!(reply.len(), 0, "should have no records");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn can_add_documents() {
|
|
|
|
|
let mut reply = Reply::new();
|
|
|
|
|
let doc = Document::new();
|
|
|
|
|
reply.add(doc.clone());
|
|
|
|
|
assert_eq!(reply.len(), 1);
|
|
|
|
|
reply.add(doc.clone());
|
|
|
|
|
assert_eq!(reply.len(), 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn can_retrieve_documents() {
|
|
|
|
|
let fieldname = Name::english("field");
|
|
|
|
|
let mut doc1 = Document::new();
|
|
|
|
|
doc1.add_field(fieldname.clone(), "one");
|
|
|
|
|
let mut doc2 = Document::new();
|
|
|
|
|
doc2.add_field(fieldname.clone(), "two");
|
|
|
|
|
let mut reply = Reply::new();
|
|
|
|
|
reply.add(doc1);
|
|
|
|
|
reply.add(doc2);
|
|
|
|
|
let mut reply_iter = reply.iter();
|
|
|
|
|
let result1 = reply_iter.next().unwrap();
|
|
|
|
|
match result1.get_field(&fieldname) {
|
|
|
|
|
CalcValue::Value(data) => match data {
|
|
|
|
|
Field::StaticString(output) => assert_eq!(output, "one"),
|
|
|
|
|
_ => unreachable!("got {:?}: should have been static string", result1),
|
|
|
|
|
},
|
|
|
|
|
_ => unreachable!("got {:?}, should have been value", result1),
|
|
|
|
|
}
|
|
|
|
|
let result2 = reply_iter.next().unwrap();
|
|
|
|
|
match result2.get_field(&fieldname) {
|
|
|
|
|
CalcValue::Value(data) => match data {
|
|
|
|
|
Field::StaticString(output) => assert_eq!(output, "two"),
|
|
|
|
|
_ => unreachable!("got {:?}: should have been static string", result2),
|
|
|
|
|
},
|
|
|
|
|
_ => unreachable!("got {:?}, should have been value", result2),
|
|
|
|
|
}
|
|
|
|
|
match reply_iter.next() {
|
|
|
|
|
None => {}
|
|
|
|
|
Some(_) => unreachable!("should be out of data"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub struct InternalRecord {
|
|
|
|
|
data: HashMap<Uuid, Field>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl InternalRecord {
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
data: HashMap::new(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn insert<F>(&mut self, id: Uuid, data: F) -> Field
|
|
|
|
|
where
|
|
|
|
|
F: Into<Field>,
|
|
|
|
|
{
|
|
|
|
|
match self.data.insert(id, data.into()) {
|
|
|
|
|
Some(data) => data.clone(),
|
|
|
|
|
None => Field::None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get(&self, id: &Uuid) -> Option<&Field> {
|
|
|
|
|
self.data.get(id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
|
self.data.is_empty()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub struct InternalRecords {
|
|
|
|
|
data: HashMap<Oid, InternalRecord>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl InternalRecords {
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
data: HashMap::new(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn insert(&mut self, oid: Oid, record: InternalRecord) -> Option<InternalRecord> {
|
|
|
|
|
self.data.insert(oid, record)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get(&self, oid: &Oid) -> Option<&InternalRecord> {
|
|
|
|
|
self.data.get(oid)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn remove(&mut self, oid: &Oid) -> Option<InternalRecord> {
|
|
|
|
|
self.data.remove(oid)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn iter(&self) -> impl Iterator<Item = (&Oid, &InternalRecord)> {
|
|
|
|
|
self.data.iter()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn keys(&self) -> impl Iterator<Item = &Oid> {
|
|
|
|
|
self.data.keys()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn values(&self) -> impl Iterator<Item = &InternalRecord> {
|
|
|
|
|
self.data.values()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn contains_key(&self, oid: &Oid) -> bool {
|
|
|
|
|
self.data.contains_key(oid)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn len(&self) -> usize {
|
|
|
|
|
self.data.len()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub struct Record {
|
|
|
|
|
names: Names,
|
|
|
|
|
data: InternalRecord,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Record {
|
|
|
|
|
fn with_data(names: Names, rec: InternalRecord) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
names: names,
|
|
|
|
|
data: rec,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get<NT>(&self, field_id: NT) -> Result<Field, MTTError>
|
|
|
|
|
where
|
|
|
|
|
NT: Into<NameType>,
|
|
|
|
|
{
|
|
|
|
|
let id = match self.names.get_id(field_id) {
|
|
|
|
|
Ok(data) => data,
|
|
|
|
|
Err(err) => return Err(err),
|
|
|
|
|
};
|
|
|
|
|
match self.data.get(&id) {
|
|
|
|
|
Some(data) => Ok(data.clone()),
|
2026-02-12 22:49:19 -05:00
|
|
|
None => Err(MTTError::new(NameType::None, ErrorID::FieldMissingData)),
|
2026-01-31 13:03:01 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub struct Records {
|
|
|
|
|
names: Names,
|
|
|
|
|
data: InternalRecords,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Records {
|
|
|
|
|
pub fn new(names: Names) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
names: names,
|
|
|
|
|
data: InternalRecords::new(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn with_data(names: Names, records: InternalRecords) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
names: names,
|
|
|
|
|
data: records,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn insert(&mut self, oid: Oid, record: InternalRecord) -> Option<InternalRecord> {
|
|
|
|
|
self.data.insert(oid, record)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
|
self.data.len()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn iter(&self) -> impl Iterator<Item = Record> {
|
|
|
|
|
RecordIter::new(self)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_internal_records(&self) -> &InternalRecords {
|
|
|
|
|
&self.data
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct RecordIter {
|
|
|
|
|
names: Names,
|
|
|
|
|
recs: Vec<InternalRecord>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl RecordIter {
|
|
|
|
|
fn new(records: &Records) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
names: records.names.clone(),
|
|
|
|
|
recs: records.data.values().cloned().collect(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Iterator for RecordIter {
|
|
|
|
|
type Item = Record;
|
|
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
|
match self.recs.pop() {
|
|
|
|
|
Some(rec) => Some(Record::with_data(self.names.clone(), rec.clone())),
|
|
|
|
|
None => None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub struct Document {
|
|
|
|
|
data: HashMap<NameType, CalcValue>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Document {
|
|
|
|
|
fn new() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
data: HashMap::new(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn add_field<NT, CV>(&mut self, name: NT, field: CV)
|
|
|
|
|
where
|
|
|
|
|
CV: Into<CalcValue>,
|
|
|
|
|
NT: Into<NameType>,
|
|
|
|
|
{
|
|
|
|
|
self.data.insert(name.into(), field.into());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_field<NT>(&self, name: NT) -> &CalcValue
|
|
|
|
|
where
|
|
|
|
|
NT: Into<NameType>,
|
|
|
|
|
{
|
|
|
|
|
match self.data.get(&name.into()) {
|
|
|
|
|
Some(data) => data,
|
|
|
|
|
None => &CalcValue::None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
fn get_all(&self) -> Vec<(NameType, Field)> {
|
|
|
|
|
let mut output = Vec::new();
|
|
|
|
|
for (key, value) in self.data.iter() {
|
|
|
|
|
output.push((key.clone(), value.get(&Field::None)));
|
|
|
|
|
}
|
|
|
|
|
output
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn iter(&self) -> impl Iterator<Item = (&NameType, &CalcValue)> {
|
|
|
|
|
self.data.iter()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod documents {
|
|
|
|
|
use super::*;
|
|
|
|
|
use crate::name::Name;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn can_add_static_string() {
|
|
|
|
|
let mut add = Document::new();
|
|
|
|
|
let name = Name::english(Uuid::new_v4().to_string().as_str());
|
|
|
|
|
let data = Uuid::new_v4().to_string();
|
|
|
|
|
add.add_field(name.clone(), data.clone());
|
|
|
|
|
let result = add.get_field(&name);
|
|
|
|
|
match result {
|
|
|
|
|
CalcValue::Value(holder) => match holder {
|
|
|
|
|
Field::StaticString(result) => assert_eq!(result, &data),
|
|
|
|
|
_ => unreachable!("got {:?}: should have received static string", holder),
|
|
|
|
|
},
|
|
|
|
|
_ => unreachable!("got {:?}, should have been value", result),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn can_add_uuid() {
|
|
|
|
|
let mut add = Document::new();
|
|
|
|
|
let name = Name::english(Uuid::new_v4().to_string().as_str());
|
|
|
|
|
let data = Uuid::new_v4();
|
|
|
|
|
add.add_field(name.clone(), data.clone());
|
|
|
|
|
let result = add.get_field(&name);
|
|
|
|
|
match result {
|
|
|
|
|
CalcValue::Value(holder) => match holder {
|
|
|
|
|
Field::Uuid(result) => assert_eq!(result, &data),
|
|
|
|
|
_ => unreachable!("got {:?}: should have received static string", holder),
|
|
|
|
|
},
|
|
|
|
|
_ => unreachable!("got {:?}, should have been value", result),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub struct Delete {
|
|
|
|
|
query: Query,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Delete {
|
|
|
|
|
pub fn new(query: Query) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
query: query.into(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_query(&self) -> &Query {
|
|
|
|
|
&self.query
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub struct Update {
|
|
|
|
|
query: Query,
|
|
|
|
|
values: Document,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Update {
|
|
|
|
|
pub fn new(query: Query) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
query: query.into(),
|
|
|
|
|
values: Document::new(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_query(&self) -> &Query {
|
|
|
|
|
&self.query
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_values(&self) -> &Document {
|
|
|
|
|
&self.values
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_values_mut(&mut self) -> &mut Document {
|
|
|
|
|
&mut self.values
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
|
|
|
|
pub struct Oid {
|
|
|
|
|
oid: Uuid,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Oid {
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
oid: Uuid::new_v4(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod oids {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn are_oids_random() {
|
|
|
|
|
let count = 10;
|
|
|
|
|
let mut holder: Vec<Oid> = Vec::new();
|
|
|
|
|
while holder.len() < count {
|
|
|
|
|
let result = Oid::new();
|
|
|
|
|
assert!(!holder.contains(&result));
|
|
|
|
|
holder.push(result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|