Got unique index working, and connected to DocDef.
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 1s

This commit is contained in:
Jeff Baskin 2025-09-14 10:53:47 -04:00
parent 7f1ce3d871
commit 32d3fbadea

View File

@ -1105,7 +1105,7 @@ impl FieldSetting {
fn set_default(&mut self, value: Option<Field>) -> Result<(), MTTError> { fn set_default(&mut self, value: Option<Field>) -> Result<(), MTTError> {
match value { match value {
Some(data) => { Some(data) => {
match self.check(Some(data.clone())) { match self.validate(Some(data.clone())) {
Ok(_) => {} Ok(_) => {}
Err(err) => return Err(err), Err(err) => return Err(err),
} }
@ -1134,7 +1134,7 @@ impl FieldSetting {
} }
} }
fn check(&self, value: Option<Field>) -> Result<Field, MTTError> { fn validate(&self, value: Option<Field>) -> Result<Field, MTTError> {
match value { match value {
Some(data) => { Some(data) => {
let vft: FieldType = (&data).into(); let vft: FieldType = (&data).into();
@ -1170,20 +1170,20 @@ mod fieldsettings {
use super::*; use super::*;
#[test] #[test]
fn checks_field_type() { fn validates_field_type() {
let fset = FieldSetting::new(FieldType::Uuid); let fset = FieldSetting::new(FieldType::Uuid);
let value: Field = Uuid::new_v4().into(); let value: Field = Uuid::new_v4().into();
match fset.check(Some(value.clone())) { match fset.validate(Some(value.clone())) {
Ok(data) => assert_eq!(data, value), Ok(data) => assert_eq!(data, value),
Err(err) => unreachable!("got {:?}: should have gotten a value", err), Err(err) => unreachable!("got {:?}: should have gotten a value", err),
} }
} }
#[test] #[test]
fn checks_for_bad_field_type() { fn validates_for_bad_field_type() {
let fset = FieldSetting::new(FieldType::Uuid); let fset = FieldSetting::new(FieldType::Uuid);
let value: Field = "text".into(); let value: Field = "text".into();
match fset.check(Some(value)) { match fset.validate(Some(value)) {
Ok(data) => unreachable!("got {:?}: should have gotten an error", data), Ok(data) => unreachable!("got {:?}: should have gotten an error", data),
Err(err) => match err { Err(err) => match err {
MTTError::DocumentFieldWrongDataType(expected, got) => { MTTError::DocumentFieldWrongDataType(expected, got) => {
@ -1198,7 +1198,7 @@ mod fieldsettings {
#[test] #[test]
fn no_default_returns_error() { fn no_default_returns_error() {
let fset = FieldSetting::new(FieldType::Uuid); let fset = FieldSetting::new(FieldType::Uuid);
match fset.check(None) { match fset.validate(None) {
Ok(data) => unreachable!("got {:?}: should have gotten an error", data), Ok(data) => unreachable!("got {:?}: should have gotten an error", data),
Err(err) => match err { Err(err) => match err {
MTTError::DocumentFieldMissing(data) => assert_eq!(data, ""), MTTError::DocumentFieldMissing(data) => assert_eq!(data, ""),
@ -1211,7 +1211,7 @@ mod fieldsettings {
fn returns_value_if_default_is_set() { fn returns_value_if_default_is_set() {
let mut fset = FieldSetting::new(FieldType::StaticString); let mut fset = FieldSetting::new(FieldType::StaticString);
fset.set_default(None); fset.set_default(None);
match fset.check(None) { match fset.validate(None) {
Ok(data) => assert_eq!(data, "".into()), Ok(data) => assert_eq!(data, "".into()),
Err(err) => unreachable!("got {:?}: should have gotten a value", err), Err(err) => unreachable!("got {:?}: should have gotten a value", err),
} }
@ -1222,19 +1222,19 @@ mod fieldsettings {
let mut fset = FieldSetting::new(FieldType::StaticString); let mut fset = FieldSetting::new(FieldType::StaticString);
let input = "fred"; let input = "fred";
fset.set_default(Some(input.into())); fset.set_default(Some(input.into()));
match fset.check(None) { match fset.validate(None) {
Ok(data) => assert_eq!(data, input.into()), Ok(data) => assert_eq!(data, input.into()),
Err(err) => unreachable!("got {:?}: should have gotten a value", err), Err(err) => unreachable!("got {:?}: should have gotten a value", err),
} }
} }
#[test] #[test]
fn checks_for_unique_value() { fn validates_for_unique_value() {
let mut fset = FieldSetting::new(FieldType::Uuid); let mut fset = FieldSetting::new(FieldType::Uuid);
let field: Field = Uuid::new_v4().into(); let field: Field = Uuid::new_v4().into();
fset.set_unique(); fset.set_unique();
fset.use_unique_value(field.clone()); fset.use_unique_value(field.clone());
match fset.check(Some(field.clone())) { match fset.validate(Some(field.clone())) {
Ok(data) => unreachable!("got {:?}: should have been error", data), Ok(data) => unreachable!("got {:?}: should have been error", data),
Err(err) => match err { Err(err) => match err {
MTTError::FieldDuplicate(key, result) => { MTTError::FieldDuplicate(key, result) => {
@ -1317,15 +1317,32 @@ mod additions {
} }
} }
#[derive(Clone, Debug)]
enum IndexType {
Index,
Unique,
}
impl IndexType {
fn create_index(&self) -> Index {
match self {
Self::Index => Index::new(),
Self::Unique => Index::new_unique(),
}
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct DocDef { struct DocDef {
fields: HashMap<String, FieldSetting>, fields: HashMap<String, FieldSetting>,
indexes: HashMap<String, IndexType>,
} }
impl DocDef { impl DocDef {
fn new() -> Self { fn new() -> Self {
Self { Self {
fields: HashMap::new(), fields: HashMap::new(),
indexes: HashMap::new(),
} }
} }
@ -1347,12 +1364,12 @@ impl DocDef {
} }
} }
fn check(&self, field_name: &str, value: Option<Field>) -> Result<Field, MTTError> { fn validate(&self, field_name: &str, value: Option<Field>) -> Result<Field, MTTError> {
let setting = match self.get_field(field_name) { let setting = match self.get_field(field_name) {
Ok(data) => data, Ok(data) => data,
Err(err) => return Err(err), Err(err) => return Err(err),
}; };
setting.check(value) setting.validate(value)
} }
fn set_default(&mut self, field_name: &str, value: Option<Field>) -> Result<(), MTTError> { fn set_default(&mut self, field_name: &str, value: Option<Field>) -> Result<(), MTTError> {
@ -1366,6 +1383,23 @@ impl DocDef {
} }
} }
fn add_index(&mut self, field_name: String, index_type: IndexType) -> Result<(), MTTError> {
let setting = match self.get_field(&field_name) {
Ok(_) => {}
Err(err) => return Err(err),
};
self.indexes.insert(field_name, index_type);
Ok(())
}
fn create_indexes(&self) -> HashMap<String, Index> {
let mut output = HashMap::new();
for (key, value) in self.indexes.iter() {
output.insert(key.clone(), value.create_index());
}
output
}
fn set_unique(&mut self, field_name: &str) -> Result<(), MTTError> { fn set_unique(&mut self, field_name: &str) -> Result<(), MTTError> {
let setting = match self.get_field_mut(field_name) { let setting = match self.get_field_mut(field_name) {
Ok(data) => data, Ok(data) => data,
@ -1380,7 +1414,7 @@ impl DocDef {
Ok(data) => data, Ok(data) => data,
Err(err) => return Err(err), Err(err) => return Err(err),
}; };
match setting.check(Some(field.clone())) { match setting.validate(Some(field.clone())) {
Ok(data) => setting.use_unique_value(field), Ok(data) => setting.use_unique_value(field),
Err(err) => return Err(err), Err(err) => return Err(err),
} }
@ -1412,7 +1446,7 @@ mod docdefs {
let field_type = FieldType::Uuid; let field_type = FieldType::Uuid;
docdef.add_field(name.clone(), field_type.clone()); docdef.add_field(name.clone(), field_type.clone());
let result = docdef.get_field(name.as_str()).unwrap(); let result = docdef.get_field(name.as_str()).unwrap();
match result.check(Some(Uuid::new_v4().into())) { match result.validate(Some(Uuid::new_v4().into())) {
Ok(_) => {} Ok(_) => {}
Err(err) => unreachable!("got {:?}: should have been a value", err), Err(err) => unreachable!("got {:?}: should have been a value", err),
} }
@ -1441,7 +1475,7 @@ mod docdefs {
} }
for name in names.iter() { for name in names.iter() {
let result = docdef.get_field(name).unwrap(); let result = docdef.get_field(name).unwrap();
match result.check(Some("".into())) { match result.validate(Some("".into())) {
Ok(_) => {} Ok(_) => {}
Err(err) => unreachable!("got {:?}: should have been a value", err), Err(err) => unreachable!("got {:?}: should have been a value", err),
} }
@ -1454,7 +1488,7 @@ mod docdefs {
let name = "defaultfunction"; let name = "defaultfunction";
docdef.add_field(name.to_string(), FieldType::StaticString); docdef.add_field(name.to_string(), FieldType::StaticString);
docdef.set_default(name, None); docdef.set_default(name, None);
match docdef.get_field(name).unwrap().check(None) { match docdef.get_field(name).unwrap().validate(None) {
Ok(data) => match data { Ok(data) => match data {
Field::StaticString(result) => assert_eq!(result, ""), Field::StaticString(result) => assert_eq!(result, ""),
_ => unreachable!("got {:?}: should return a static string", data), _ => unreachable!("got {:?}: should return a static string", data),
@ -1557,7 +1591,7 @@ enum Operand {
} }
impl Operand { impl Operand {
fn check(&self, x: &Field, y: &Field) -> bool { fn validate(&self, x: &Field, y: &Field) -> bool {
match self { match self {
Self::Equal => x == y, Self::Equal => x == y,
} }
@ -1571,7 +1605,7 @@ mod operands {
#[test] #[test]
fn equals_true() { fn equals_true() {
let data: Field = Uuid::new_v4().into(); let data: Field = Uuid::new_v4().into();
assert!(Operand::Equal.check(&data, &data)); assert!(Operand::Equal.validate(&data, &data));
} }
#[test] #[test]
@ -1581,7 +1615,7 @@ mod operands {
while x == y { while x == y {
y = Uuid::new_v4().into(); y = Uuid::new_v4().into();
} }
assert!(!Operand::Equal.check(&x, &y)); assert!(!Operand::Equal.validate(&x, &y));
} }
} }
@ -1608,8 +1642,8 @@ impl Specifier {
self.field_name.clone() self.field_name.clone()
} }
fn check(&self, field: &Field) -> bool { fn validate(&self, field: &Field) -> bool {
self.operation.check(field, &self.value) self.operation.validate(field, &self.value)
} }
} }
@ -1813,31 +1847,44 @@ impl Oid {
struct Index { struct Index {
data: HashMap<Field, HashSet<Oid>>, data: HashMap<Field, HashSet<Oid>>,
unique: bool,
} }
impl Index { impl Index {
fn new() -> Self { fn new() -> Self {
Self { Self {
data: HashMap::new(), data: HashMap::new(),
unique: false,
} }
} }
fn add(&mut self, field: Field, oid: Oid) { fn new_unique() -> Self {
Self {
data: HashMap::new(),
unique: true,
}
}
fn add(&mut self, field: Field, oid: Oid) -> Result<(), MTTError> {
let oids = match self.data.get_mut(&field) { let oids = match self.data.get_mut(&field) {
Some(data) => data, Some(data) => data,
None => { None => {
let mut data = HashSet::new(); self.data.insert(field.clone(), HashSet::new());
self.data.insert(field.clone(), data);
self.data.get_mut(&field).unwrap() self.data.get_mut(&field).unwrap()
} }
}; };
if self.unique && oids.len() > 0 {
return Err(MTTError::FieldDuplicate("".to_string(), field));
} else {
oids.insert(oid); oids.insert(oid);
} }
Ok(())
}
fn get(&self, spec: &Specifier) -> Vec<Oid> { fn get(&self, spec: &Specifier) -> Vec<Oid> {
let mut output = Vec::new(); let mut output = Vec::new();
for (field, oids) in self.data.iter() { for (field, oids) in self.data.iter() {
if spec.check(field) { if spec.validate(field) {
for oid in oids.iter() { for oid in oids.iter() {
output.push(oid.clone()); output.push(oid.clone());
} }
@ -1857,6 +1904,16 @@ impl Index {
None => {} None => {}
}; };
} }
fn validate(&self, field: &Field) -> Result<(), MTTError> {
if self.unique {
match self.data.get(field) {
Some(_) => return Err(MTTError::FieldDuplicate("".to_string(), field.clone())),
None => {}
}
}
Ok(())
}
} }
#[cfg(test)] #[cfg(test)]
@ -1944,11 +2001,60 @@ mod indexes {
index.remove(&field, &oid); index.remove(&field, &oid);
assert_eq!(index.data.len(), 0); assert_eq!(index.data.len(), 0);
} }
#[test]
fn do_unique_indexes_error_on_duplicates() {
let mut index = Index::new_unique();
let field: Field = "fred".into();
let oids = get_oids(2);
index.add(field.clone(), oids[0].clone()).unwrap();
match index.add(field.clone(), oids[0].clone()) {
Ok(_) => unreachable!("should have been an error"),
Err(err) => match err {
MTTError::FieldDuplicate(field_name, value) => {
assert_eq!(field_name, "");
assert_eq!(value, field);
}
_ => unreachable!("got {:?}: should have been duplicate field", err),
},
}
}
#[test]
fn index_returns_validate() {
let mut index = Index::new();
let field: Field = "stuff".into();
let oid = Oid::new();
index.add(field.clone(), oid).unwrap();
match index.validate(&field) {
Ok(_) => {}
Err(err) => unreachable!("got {:?}: should have returned without issue", err),
}
}
#[test]
fn unique_return_duplicate_error() {
let mut index = Index::new_unique();
let field: Field = "fred".into();
let oid = Oid::new();
index.add(field.clone(), oid).unwrap();
match index.validate(&field) {
Ok(_) => unreachable!("should have gotten a duplication error"),
Err(err) => match err {
MTTError::FieldDuplicate(field_name, value) => {
assert_eq!(field_name, "");
assert_eq!(value, field);
}
_ => unreachable!("got {:?}: should have been duplicate field", err),
},
}
}
} }
struct DocumentFile { struct DocumentFile {
docdef: DocDef, docdef: DocDef,
docs: HashMap<Oid, Document>, docs: HashMap<Oid, Document>,
indexes: HashMap<String, Index>,
queue: Queue, queue: Queue,
rx: Receiver<Message>, rx: Receiver<Message>,
} }
@ -1956,8 +2062,9 @@ struct DocumentFile {
impl DocumentFile { impl DocumentFile {
fn new(queue: Queue, rx: Receiver<Message>, docdef: DocDef) -> Self { fn new(queue: Queue, rx: Receiver<Message>, docdef: DocDef) -> Self {
Self { Self {
docdef: docdef, docdef: docdef.clone(),
docs: HashMap::new(), docs: HashMap::new(),
indexes: docdef.create_indexes(),
queue: queue, queue: queue,
rx: rx, rx: rx,
} }
@ -2046,7 +2153,7 @@ impl DocumentFile {
let mut holder = Document::new(); let mut holder = Document::new();
let doc = addition.get_document(); let doc = addition.get_document();
for (key, value) in doc.iter() { for (key, value) in doc.iter() {
match self.docdef.check(key, Some(value.clone())) { match self.docdef.validate(key, Some(value.clone())) {
Ok(data) => holder.add_field(key.clone(), value.clone()), Ok(data) => holder.add_field(key.clone(), value.clone()),
Err(err) => return Self::add_field_to_error(key.to_string(), err).into(), Err(err) => return Self::add_field_to_error(key.to_string(), err).into(),
} }
@ -2054,7 +2161,7 @@ impl DocumentFile {
for (key, value) in self.docdef.iter() { for (key, value) in self.docdef.iter() {
match holder.get_field(key) { match holder.get_field(key) {
Some(_) => {} Some(_) => {}
None => match value.check(None) { None => match value.validate(None) {
Ok(data) => holder.add_field(key.clone(), data.clone()), Ok(data) => holder.add_field(key.clone(), data.clone()),
Err(err) => return Self::add_field_to_error(key.to_string(), err).into(), Err(err) => return Self::add_field_to_error(key.to_string(), err).into(),
}, },
@ -2078,7 +2185,7 @@ impl DocumentFile {
for specifier in query.iter() { for specifier in query.iter() {
match self match self
.docdef .docdef
.check(&specifier.field_name, Some(specifier.value.clone())) .validate(&specifier.field_name, Some(specifier.value.clone()))
{ {
Ok(_) => {} Ok(_) => {}
Err(err) => match err { Err(err) => match err {
@ -2125,7 +2232,7 @@ impl DocumentFile {
for oid in oids.iter() { for oid in oids.iter() {
let doc = self.docs.get_mut(oid).unwrap(); let doc = self.docs.get_mut(oid).unwrap();
for (key, value) in update.get_values().iter() { for (key, value) in update.get_values().iter() {
match self.docdef.check(key, Some(value.clone())) { match self.docdef.validate(key, Some(value.clone())) {
Ok(field) => { Ok(field) => {
self.docdef self.docdef
.remove_unique_value(key, doc.get_field(key).unwrap()); .remove_unique_value(key, doc.get_field(key).unwrap());
@ -3044,7 +3151,7 @@ mod document_files {
} }
} }
// #[test] //#[test]
fn unique_available_after_bad_change() { fn unique_available_after_bad_change() {
let mut ids: Vec<Uuid> = Vec::new(); let mut ids: Vec<Uuid> = Vec::new();
while ids.len() < 3 { while ids.len() < 3 {