use std::sync::Arc;
use crate::collection::Document;
use crate::common::Fields;
use crate::errors::{ErrorKind, NitriteError, NitriteResult};
use crate::repository::{EntityId, EntityIndex, NitriteEntity};
use crate::{Convertible, Value};
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct IndexDescriptor {
inner: Arc<IndexDescriptorInner>,
}
impl IndexDescriptor {
pub fn new(
index_type: &str,
index_fields: Fields,
collection_name: &str,
) -> Self {
Self {
inner: Arc::new(IndexDescriptorInner {
index_type: index_type.to_string(),
index_fields,
collection_name: collection_name.to_string(),
}),
}
}
pub fn index_type(&self) -> String {
self.inner.index_type.clone()
}
pub fn index_fields(&self) -> Fields {
self.inner.index_fields.clone()
}
pub fn collection_name(&self) -> String {
self.inner.collection_name.clone()
}
pub fn is_compound_index(&self) -> bool {
self.inner.index_fields.field_names().len() > 1
}
}
#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct IndexDescriptorInner {
index_type: String,
index_fields: Fields,
collection_name: String,
}
impl IndexDescriptorInner {
pub(crate) fn new(
index_type: String,
index_fields: Fields,
collection_name: String,
) -> Self {
Self {
index_type,
index_fields,
collection_name,
}
}
}
impl Convertible for IndexDescriptor {
type Output = Self;
fn to_value(&self) -> NitriteResult<Value> {
let mut doc = Document::new();
doc.put("index_type", Value::String(self.index_type()))?;
doc.put("index_fields", self.index_fields().to_value()?)?;
doc.put("collection_name", Value::String(self.collection_name()))?;
Ok(Value::Document(doc))
}
fn from_value(value: &Value) -> NitriteResult<Self::Output> {
match value {
Value::Document(doc) => {
let index_type = doc.get("index_type")?
.as_string()
.ok_or_else(|| NitriteError::new(
"Index descriptor deserialization error: 'index_type' field is missing or not a string",
ErrorKind::ObjectMappingError
))?
.clone();
let index_fields = doc.get("index_fields")?;
let index_fields = Fields::from_value(&index_fields)?;
let collection_name = doc.get("collection_name")?
.as_string()
.ok_or_else(|| NitriteError::new(
"Index descriptor deserialization error: 'collection_name' field is missing or not a string",
ErrorKind::ObjectMappingError
))?
.clone();
Ok(IndexDescriptor::new(&index_type, index_fields, &collection_name))
}
_ => {
log::error!("Failed to create IndexDescriptor from Value {:?}", value);
Err(NitriteError::new(
"Index descriptor deserialization error: expected document value but found another type",
ErrorKind::ObjectMappingError,
))
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Value;
#[test]
fn test_index_descriptor_new() {
let fields = Fields::with_names(vec!["field1", "field2"]).expect("Failed to create fields");
let descriptor = IndexDescriptor::new("type1", fields.clone(), "collection1");
assert_eq!(descriptor.index_type(), "type1");
assert_eq!(descriptor.index_fields(), fields);
assert_eq!(descriptor.collection_name(), "collection1");
}
#[test]
fn test_is_compound_index() {
let single_field = Fields::with_names(vec!["field1"]).expect("Failed to create fields");
let compound_field = Fields::with_names(vec!["field1", "field2"]).expect("Failed to create fields");
let single_descriptor = IndexDescriptor::new("type1", single_field, "collection1");
let compound_descriptor = IndexDescriptor::new("type1", compound_field, "collection1");
assert!(!single_descriptor.is_compound_index());
assert!(compound_descriptor.is_compound_index());
}
#[test]
fn test_to_value() {
let fields = Fields::with_names(vec!["field1"]).expect("Failed to create fields");
let descriptor = IndexDescriptor::new("type1", fields.clone(), "collection1");
let value = descriptor.to_value().unwrap();
if let Value::Document(doc) = value {
assert_eq!(doc.get("index_type").unwrap().as_string().unwrap(), "type1");
assert_eq!(doc.get("collection_name").unwrap().as_string().unwrap(), "collection1");
assert_eq!(Fields::from_value(&doc.get("index_fields").unwrap()).unwrap(), fields);
} else {
panic!("Expected Value::Document");
}
}
#[test]
fn test_from_value() {
let fields = Fields::with_names(vec!["field1"]).expect("Failed to create fields");
let mut doc = Document::new();
doc.put("index_type", Value::String("type1".to_string())).unwrap();
doc.put("index_fields", fields.to_value().unwrap()).unwrap();
doc.put("collection_name", Value::String("collection1".to_string())).unwrap();
let value = Value::Document(doc);
let descriptor = IndexDescriptor::from_value(&value).unwrap();
assert_eq!(descriptor.index_type(), "type1");
assert_eq!(descriptor.index_fields(), fields);
assert_eq!(descriptor.collection_name(), "collection1");
}
#[test]
fn test_from_value_invalid() {
let value = Value::String("invalid".to_string());
let result = IndexDescriptor::from_value(&value);
assert!(result.is_err());
}
#[test]
fn test_from_value_missing_fields() {
let mut doc = Document::new();
doc.put("index_type", Value::String("type1".to_string())).unwrap();
let value = Value::Document(doc);
let result = IndexDescriptor::from_value(&value);
assert!(result.is_err());
}
#[test]
fn test_from_value_non_string_index_type() {
let fields = Fields::with_names(vec!["field1"]).unwrap();
let mut doc = Document::new();
doc.put("index_type", Value::I32(123)).unwrap(); doc.put("index_fields", fields.to_value().unwrap()).unwrap();
doc.put("collection_name", Value::String("collection1".to_string())).unwrap();
let value = Value::Document(doc);
let result = IndexDescriptor::from_value(&value);
assert!(result.is_err());
}
#[test]
fn test_from_value_non_string_collection_name() {
let fields = Fields::with_names(vec!["field1"]).unwrap();
let mut doc = Document::new();
doc.put("index_type", Value::String("type1".to_string())).unwrap();
doc.put("index_fields", fields.to_value().unwrap()).unwrap();
doc.put("collection_name", Value::Array(vec![])).unwrap();
let value = Value::Document(doc);
let result = IndexDescriptor::from_value(&value);
assert!(result.is_err());
}
#[test]
fn test_from_value_with_correct_types() {
let fields = Fields::with_names(vec!["field1"]).unwrap();
let mut doc = Document::new();
doc.put("index_type", Value::String("UNIQUE".to_string())).unwrap();
doc.put("index_fields", fields.to_value().unwrap()).unwrap();
doc.put("collection_name", Value::String("test_collection".to_string())).unwrap();
let value = Value::Document(doc);
let result = IndexDescriptor::from_value(&value);
assert!(result.is_ok());
let descriptor = result.unwrap();
assert_eq!(descriptor.index_type(), "UNIQUE");
assert_eq!(descriptor.collection_name(), "test_collection");
}
#[test]
fn test_round_trip_to_from_value() {
let fields = Fields::with_names(vec!["field1", "field2"]).unwrap();
let original = IndexDescriptor::new("COMPOUND", fields.clone(), "my_collection");
let value = original.to_value().unwrap();
let recovered = IndexDescriptor::from_value(&value).unwrap();
assert_eq!(recovered.index_type(), original.index_type());
assert_eq!(recovered.index_fields(), original.index_fields());
assert_eq!(recovered.collection_name(), original.collection_name());
}
}