use super::migration::{MigrationArguments, MigrationStep};
use crate::{
collection::Document,
common::Value,
errors::{ErrorKind, NitriteError, NitriteResult},
nitrite::Nitrite,
};
use std::{
any::Any
,
sync::{Arc, Mutex},
};
#[derive(Clone)]
pub enum MigrationFn {
CustomInstruction(Arc<dyn Fn(Nitrite) -> NitriteResult<()> + Send + Sync + 'static>),
ValueConverter(Arc<dyn Fn(Value) -> NitriteResult<Value> + Send + Sync + 'static>),
FieldGenerator(Arc<dyn Fn(Document) -> NitriteResult<Value> + Send + Sync + 'static>),
}
impl MigrationFn {
pub fn custom_instruction<F>(f: F) -> Self
where
F: Fn(Nitrite) -> NitriteResult<()> + Send + Sync + 'static,
{
MigrationFn::CustomInstruction(Arc::new(f))
}
pub fn value_converter<F>(f: F) -> Self
where
F: Fn(Value) -> NitriteResult<Value> + Send + Sync + 'static,
{
MigrationFn::ValueConverter(Arc::new(f))
}
pub fn field_generator<F>(f: F) -> Self
where
F: Fn(Document) -> NitriteResult<Value> + Send + Sync + 'static,
{
MigrationFn::FieldGenerator(Arc::new(f))
}
pub fn call_custom_instruction(&self, nitrite: Nitrite) -> NitriteResult<()> {
match self {
MigrationFn::CustomInstruction(f) => f(nitrite),
_ => Err(NitriteError::new(
"Expected CustomInstruction function",
ErrorKind::ValidationError,
)),
}
}
pub fn call_value_converter(&self, value: Value) -> NitriteResult<Value> {
match self {
MigrationFn::ValueConverter(f) => f(value),
_ => Err(NitriteError::new(
"Expected ValueConverter function",
ErrorKind::ValidationError,
)),
}
}
pub fn call_field_generator(&self, doc: Document) -> NitriteResult<Value> {
match self {
MigrationFn::FieldGenerator(f) => f(doc),
_ => Err(NitriteError::new(
"Expected FieldGenerator function",
ErrorKind::ValidationError,
)),
}
}
}
#[derive(Clone)]
pub struct InstructionSet {
migration_steps: Arc<Mutex<Vec<MigrationStep>>>,
}
impl InstructionSet {
pub fn new(steps: Vec<MigrationStep>) -> Self {
InstructionSet {
migration_steps: Arc::new(Mutex::new(steps)),
}
}
pub fn get_steps(&self) -> NitriteResult<Vec<MigrationStep>> {
let steps = self.migration_steps.lock().unwrap();
Ok(steps.clone())
}
pub fn for_database(&self) -> DatabaseInstructionBuilder {
DatabaseInstructionBuilder::new(self.migration_steps.clone())
}
pub fn for_collection(&self, name: &str) -> CollectionInstructionBuilder {
CollectionInstructionBuilder::new(name.to_string(), self.migration_steps.clone())
}
pub fn for_repository(
&self,
entity_name: &str,
key: Option<&str>,
) -> RepositoryInstructionBuilder {
RepositoryInstructionBuilder::new(
entity_name.to_string(),
key.map(|s| s.to_string()),
self.migration_steps.clone(),
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InstructionType {
AddUser,
ChangePassword,
DropCollection,
DropRepository,
CustomInstruction,
CollectionRename,
AddField,
RenameField,
DeleteField,
DropIndex,
DropAllIndices,
CreateIndex,
RepositoryRename,
RepositoryAddField,
RepositoryRenameField,
RepositoryDeleteField,
RepositoryChangeDataType,
RepositoryChangeIdField,
RepositoryDropIndex,
RepositoryDropAllIndices,
RepositoryCreateIndex,
}
pub trait Instruction: Send + Sync {
fn instruction_type(&self) -> InstructionType;
}
pub trait DatabaseInstruction: Instruction {
fn steps(&self) -> NitriteResult<Vec<MigrationStep>>;
}
pub trait CollectionInstruction: Instruction {
fn steps(&self) -> NitriteResult<Vec<MigrationStep>>;
fn collection_name(&self) -> &str;
}
pub trait RepositoryInstruction: Instruction {
fn steps(&self) -> NitriteResult<Vec<MigrationStep>>;
fn entity_name(&self) -> &str;
fn key(&self) -> Option<&str>;
}
pub struct DatabaseInstructionBuilder {
steps: Arc<Mutex<Vec<MigrationStep>>>,
}
impl DatabaseInstructionBuilder {
pub fn new(steps: Arc<Mutex<Vec<MigrationStep>>>) -> Self {
DatabaseInstructionBuilder { steps }
}
fn add_step(&mut self, step: MigrationStep) {
let mut steps = self.steps.lock().unwrap();
steps.push(step);
}
pub fn add_user(&mut self, username: &str, password: &str) -> &mut Self {
let step = MigrationStep {
instruction_type: InstructionType::AddUser,
collection_name: None,
entity_name: None,
key: None,
arguments: MigrationArguments::Double(
Arc::new(username.to_string()),
Arc::new(password.to_string()),
),
};
self.add_step(step);
self
}
pub fn change_password(&mut self, username: &str, old_pw: &str, new_pw: &str) -> &mut Self {
let step = MigrationStep {
instruction_type: InstructionType::ChangePassword,
collection_name: None,
entity_name: None,
key: None,
arguments: MigrationArguments::Triple(
Arc::new(username.to_string()),
Arc::new(old_pw.to_string()),
Arc::new(new_pw.to_string()),
),
};
self.add_step(step);
self
}
pub fn drop_collection(&mut self, collection_name: &str) -> &mut Self {
let step = MigrationStep {
instruction_type: InstructionType::DropCollection,
collection_name: Some(collection_name.to_string()),
entity_name: None,
key: None,
arguments: MigrationArguments::Single(Arc::new(collection_name.to_string())),
};
self.add_step(step);
self
}
pub fn drop_repository(&mut self, entity_name: &str, key: Option<&str>) -> &mut Self {
let step = MigrationStep {
instruction_type: InstructionType::DropRepository,
collection_name: None,
entity_name: Some(entity_name.to_string()),
key: key.map(|k| k.to_string()),
arguments: if let Some(k) = key {
MigrationArguments::Double(
Arc::new(entity_name.to_string()),
Arc::new(k.to_string()),
)
} else {
MigrationArguments::Single(Arc::new(entity_name.to_string()))
},
};
self.add_step(step);
self
}
pub fn custom_instruction<F>(&mut self, instruction: F) -> &mut Self
where
F: Fn(Nitrite) -> NitriteResult<()> + Send + Sync + 'static,
{
let step = MigrationStep {
instruction_type: InstructionType::CustomInstruction,
collection_name: None,
entity_name: None,
key: None,
arguments: MigrationArguments::Single(Arc::new(MigrationFn::custom_instruction(instruction))),
};
self.add_step(step);
self
}
}
pub struct CollectionInstructionBuilder {
collection_name: String,
steps: Arc<Mutex<Vec<MigrationStep>>>,
}
impl CollectionInstructionBuilder {
pub fn new(collection_name: String, steps: Arc<Mutex<Vec<MigrationStep>>>) -> Self {
CollectionInstructionBuilder {
collection_name,
steps,
}
}
fn add_step(&mut self, step: MigrationStep) {
let mut steps = self.steps.lock().unwrap();
steps.push(step);
}
pub fn rename(&mut self, name: &str) -> &mut Self {
let step = MigrationStep {
instruction_type: InstructionType::CollectionRename,
collection_name: Some(self.collection_name.clone()),
entity_name: None,
key: None,
arguments: MigrationArguments::Single(Arc::new(name.to_string())),
};
self.add_step(step);
self
}
pub fn add_field(
&mut self,
field_name: &str,
default_value: Option<Value>,
generator: Option<impl Fn(Document) -> NitriteResult<Value> + Send + Sync + 'static>,
) -> &mut Self {
let step = MigrationStep {
instruction_type: InstructionType::AddField,
collection_name: Some(self.collection_name.clone()),
entity_name: None,
key: None,
arguments: if let Some(val) = default_value {
MigrationArguments::Double(Arc::new(field_name.to_string()), Arc::new(val))
} else if let Some(gen) = generator {
MigrationArguments::Double(Arc::new(field_name.to_string()), Arc::new(MigrationFn::field_generator(gen)))
} else {
MigrationArguments::Single(Arc::new(field_name.to_string()))
},
};
self.add_step(step);
self
}
pub fn rename_field(&mut self, old_name: &str, new_name: &str) -> &mut Self {
let step = MigrationStep {
instruction_type: InstructionType::RenameField,
collection_name: Some(self.collection_name.clone()),
entity_name: None,
key: None,
arguments: MigrationArguments::Double(
Arc::new(old_name.to_string()),
Arc::new(new_name.to_string()),
),
};
self.add_step(step);
self
}
pub fn delete_field(&mut self, field_name: &str) -> &mut Self {
let step = MigrationStep {
instruction_type: InstructionType::DeleteField,
collection_name: Some(self.collection_name.clone()),
entity_name: None,
key: None,
arguments: MigrationArguments::Single(Arc::new(field_name.to_string())),
};
self.add_step(step);
self
}
pub fn drop_index(&mut self, field_names: &[&str]) -> &mut Self {
let step = MigrationStep {
instruction_type: InstructionType::DropIndex,
collection_name: Some(self.collection_name.clone()),
entity_name: None,
key: None,
arguments: MigrationArguments::Single(Arc::new(
field_names
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>(),
)),
};
self.add_step(step);
self
}
pub fn drop_all_indices(&mut self) -> &mut Self {
let step = MigrationStep {
instruction_type: InstructionType::DropAllIndices,
collection_name: Some(self.collection_name.clone()),
entity_name: None,
key: None,
arguments: MigrationArguments::None,
};
self.add_step(step);
self
}
pub fn create_index(&mut self, index_type: &str, field_names: &[&str]) -> &mut Self {
let step = MigrationStep {
instruction_type: InstructionType::CreateIndex,
collection_name: Some(self.collection_name.clone()),
entity_name: None,
key: None,
arguments: MigrationArguments::Double(
Arc::new(index_type.to_string()),
Arc::new(
field_names
.iter()
.map(|f| f.to_string())
.collect::<Vec<_>>(),
),
),
};
self.add_step(step);
self
}
pub fn collection_name(&self) -> &str {
&self.collection_name
}
}
pub struct RepositoryInstructionBuilder {
entity_name: String,
key: Option<String>,
steps: Arc<Mutex<Vec<MigrationStep>>>,
}
impl RepositoryInstructionBuilder {
pub fn new(
entity_name: String,
key: Option<String>,
steps: Arc<Mutex<Vec<MigrationStep>>>,
) -> Self {
RepositoryInstructionBuilder {
entity_name,
key,
steps,
}
}
fn add_step(&mut self, step: MigrationStep) {
let mut steps = self.steps.lock().unwrap();
steps.push(step);
}
pub fn rename_repository(&mut self, new_entity_name: &str, new_key: Option<&str>) -> &mut Self {
let step = MigrationStep {
instruction_type: InstructionType::RepositoryRename,
collection_name: None,
entity_name: Some(self.entity_name.clone()),
key: self.key.clone(),
arguments: {
MigrationArguments::Double(
Arc::new(new_entity_name.to_string()),
Arc::new(new_key.map(|s| s.to_string())),
)
},
};
self.add_step(step);
self
}
pub fn add_field(
&mut self,
field_name: &str,
default_value: Option<Value>,
generator: Option<impl Fn(Document) -> NitriteResult<Value> + Send + Sync + 'static>,
) -> &mut Self {
let step = MigrationStep {
instruction_type: InstructionType::RepositoryAddField,
collection_name: None,
entity_name: Some(self.entity_name.clone()),
key: self.key.clone(),
arguments: if let Some(val) = default_value {
MigrationArguments::Double(Arc::new(field_name.to_string()), Arc::new(val))
} else if let Some(gen) = generator {
MigrationArguments::Double(Arc::new(field_name.to_string()), Arc::new(MigrationFn::field_generator(gen)))
} else {
MigrationArguments::Single(Arc::new(field_name.to_string()))
},
};
self.add_step(step);
self
}
pub fn rename_field(&mut self, old_name: &str, new_name: &str) -> &mut Self {
let step = MigrationStep {
instruction_type: InstructionType::RepositoryRenameField,
collection_name: None,
entity_name: Some(self.entity_name.clone()),
key: self.key.clone(),
arguments: MigrationArguments::Double(
Arc::new(old_name.to_string()),
Arc::new(new_name.to_string()),
),
};
self.add_step(step);
self
}
pub fn delete_field(&mut self, field_name: &str) -> &mut Self {
let step = MigrationStep {
instruction_type: InstructionType::RepositoryDeleteField,
collection_name: None,
entity_name: Some(self.entity_name.clone()),
key: self.key.clone(),
arguments: MigrationArguments::Single(Arc::new(field_name.to_string())),
};
self.add_step(step);
self
}
pub fn change_data_type(
&mut self,
field_name: &str,
converter: impl Fn(Value) -> NitriteResult<Value> + Send + Sync + 'static,
) -> &mut Self {
let step = MigrationStep {
instruction_type: InstructionType::RepositoryChangeDataType,
collection_name: None,
entity_name: Some(self.entity_name.clone()),
key: self.key.clone(),
arguments: MigrationArguments::Double(
Arc::new(field_name.to_string()),
Arc::new(MigrationFn::value_converter(converter)),
),
};
self.add_step(step);
self
}
pub fn change_id_field(
&mut self,
old_field_names: &[&str],
new_field_names: &[&str],
) -> &mut Self {
let step = MigrationStep {
instruction_type: InstructionType::RepositoryChangeIdField,
collection_name: None,
entity_name: Some(self.entity_name.clone()),
key: self.key.clone(),
arguments: MigrationArguments::Double(
Arc::new(
old_field_names
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>(),
),
Arc::new(
new_field_names
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>(),
),
),
};
self.add_step(step);
self
}
pub fn drop_index(&mut self, field_names: &[&str]) -> &mut Self {
let step = MigrationStep {
instruction_type: InstructionType::RepositoryDropIndex,
collection_name: None,
entity_name: Some(self.entity_name.clone()),
key: self.key.clone(),
arguments: MigrationArguments::Single(Arc::new(
field_names
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>(),
)),
};
self.add_step(step);
self
}
pub fn drop_all_indices(&mut self) -> &mut Self {
let step = MigrationStep {
instruction_type: InstructionType::RepositoryDropAllIndices,
collection_name: None,
entity_name: Some(self.entity_name.clone()),
key: self.key.clone(),
arguments: MigrationArguments::None,
};
self.add_step(step);
self
}
pub fn create_index(&mut self, index_type: &str, field_names: &[&str]) -> &mut Self {
let step = MigrationStep {
instruction_type: InstructionType::RepositoryCreateIndex,
collection_name: None,
entity_name: Some(self.entity_name.clone()),
key: self.key.clone(),
arguments: MigrationArguments::Double(
Arc::new(index_type.to_string()),
Arc::new(
field_names
.iter()
.map(|f| f.to_string())
.collect::<Vec<_>>(),
),
),
};
self.add_step(step);
self
}
pub fn entity_name(&self) -> &str {
&self.entity_name
}
pub fn key(&self) -> Option<&str> {
self.key.as_deref()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_instruction_set_new_empty() {
let set = InstructionSet::new(vec![]);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 0);
}
#[test]
fn test_instruction_set_new_with_steps() {
let step = MigrationStep {
instruction_type: InstructionType::AddUser,
collection_name: None,
entity_name: None,
key: None,
arguments: MigrationArguments::Double(
Arc::new("test_user".to_string()),
Arc::new("password".to_string()),
),
};
let set = InstructionSet::new(vec![step]);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::AddUser);
}
#[test]
fn test_instruction_set_for_database() {
let set = InstructionSet::new(vec![]);
let builder = set.for_database();
assert!(builder.steps.lock().is_ok());
}
#[test]
fn test_instruction_set_for_collection() {
let set = InstructionSet::new(vec![]);
let builder = set.for_collection("test_collection");
assert_eq!(builder.collection_name, "test_collection");
}
#[test]
fn test_instruction_set_for_repository_with_key() {
let set = InstructionSet::new(vec![]);
let builder = set.for_repository("TestEntity", Some("id"));
assert_eq!(builder.entity_name, "TestEntity");
assert_eq!(builder.key, Some("id".to_string()));
}
#[test]
fn test_instruction_set_for_repository_without_key() {
let set = InstructionSet::new(vec![]);
let builder = set.for_repository("TestEntity", None);
assert_eq!(builder.entity_name, "TestEntity");
assert_eq!(builder.key, None);
}
#[test]
fn test_instruction_type_equality() {
assert_eq!(InstructionType::AddUser, InstructionType::AddUser);
assert_ne!(InstructionType::AddUser, InstructionType::ChangePassword);
}
#[test]
fn test_instruction_type_debug() {
let it = InstructionType::AddUser;
let debug_str = format!("{:?}", it);
assert!(debug_str.contains("AddUser"));
}
#[test]
fn test_database_builder_add_user() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_database();
builder.add_user("admin", "secret");
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::AddUser);
assert_eq!(steps[0].collection_name, None);
assert_eq!(steps[0].entity_name, None);
}
#[test]
fn test_database_builder_add_user_chainable() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_database();
builder
.add_user("user1", "pass1")
.add_user("user2", "pass2");
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 2);
assert_eq!(steps[0].instruction_type, InstructionType::AddUser);
assert_eq!(steps[1].instruction_type, InstructionType::AddUser);
}
#[test]
fn test_database_builder_change_password() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_database();
builder.change_password("admin", "old", "new");
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::ChangePassword);
}
#[test]
fn test_database_builder_drop_collection() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_database();
builder.drop_collection("users");
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::DropCollection);
assert_eq!(steps[0].collection_name, Some("users".to_string()));
}
#[test]
fn test_database_builder_drop_repository_with_key() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_database();
builder.drop_repository("UserRepository", Some("userId"));
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::DropRepository);
assert_eq!(steps[0].entity_name, Some("UserRepository".to_string()));
}
#[test]
fn test_database_builder_drop_repository_without_key() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_database();
builder.drop_repository("UserRepository", None);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::DropRepository);
}
#[test]
fn test_database_builder_custom_instruction() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_database();
builder.custom_instruction(|_db| Ok(()));
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::CustomInstruction);
}
#[test]
fn test_database_builder_multiple_operations() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_database();
builder
.add_user("admin", "pass")
.change_password("admin", "pass", "newpass")
.drop_collection("temp")
.custom_instruction(|_| Ok(()));
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 4);
assert_eq!(steps[0].instruction_type, InstructionType::AddUser);
assert_eq!(steps[1].instruction_type, InstructionType::ChangePassword);
assert_eq!(steps[2].instruction_type, InstructionType::DropCollection);
assert_eq!(steps[3].instruction_type, InstructionType::CustomInstruction);
}
#[test]
fn test_collection_builder_new() {
let set = InstructionSet::new(vec![]);
let builder = set.for_collection("test_col");
assert_eq!(builder.collection_name(), "test_col");
}
#[test]
fn test_collection_builder_rename() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_collection("old_name");
builder.rename("new_name");
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::CollectionRename);
assert_eq!(steps[0].collection_name, Some("old_name".to_string()));
}
#[test]
fn test_collection_builder_add_field_with_default() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_collection("users");
builder.add_field("age", Some(Value::from(0)), None::<fn(Document) -> NitriteResult<Value>>);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::AddField);
assert_eq!(steps[0].collection_name, Some("users".to_string()));
}
#[test]
fn test_collection_builder_add_field_without_default() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_collection("users");
builder.add_field("email", None, None::<fn(Document) -> NitriteResult<Value>>);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::AddField);
}
#[test]
fn test_collection_builder_add_field_with_generator() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_collection("users");
builder.add_field(
"id",
None,
Some(|_doc| Ok(Value::from("generated"))),
);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::AddField);
}
#[test]
fn test_collection_builder_rename_field() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_collection("users");
builder.rename_field("old_field", "new_field");
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::RenameField);
}
#[test]
fn test_collection_builder_delete_field() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_collection("users");
builder.delete_field("unused_field");
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::DeleteField);
}
#[test]
fn test_collection_builder_drop_index_specific() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_collection("users");
builder.drop_index(&["email", "name"]);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::DropIndex);
}
#[test]
fn test_collection_builder_drop_index_empty() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_collection("users");
builder.drop_index(&[]);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::DropIndex);
}
#[test]
fn test_collection_builder_drop_all_indices() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_collection("users");
builder.drop_all_indices();
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::DropAllIndices);
}
#[test]
fn test_collection_builder_create_index() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_collection("users");
builder.create_index("UNIQUE", &["email"]);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::CreateIndex);
}
#[test]
fn test_collection_builder_create_index_multiple_fields() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_collection("users");
builder.create_index("COMPOUND", &["firstName", "lastName"]);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::CreateIndex);
}
#[test]
fn test_collection_builder_chained_operations() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_collection("users");
builder
.add_field("age", Some(Value::from(0)), None::<fn(Document) -> NitriteResult<Value>>)
.rename_field("email_addr", "email")
.create_index("UNIQUE", &["email"])
.delete_field("temp");
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 4);
assert_eq!(steps[0].instruction_type, InstructionType::AddField);
assert_eq!(steps[1].instruction_type, InstructionType::RenameField);
assert_eq!(steps[2].instruction_type, InstructionType::CreateIndex);
assert_eq!(steps[3].instruction_type, InstructionType::DeleteField);
}
#[test]
fn test_repository_builder_new_with_key() {
let set = InstructionSet::new(vec![]);
let builder = set.for_repository("UserRepo", Some("id"));
assert_eq!(builder.entity_name(), "UserRepo");
assert_eq!(builder.key(), Some("id"));
}
#[test]
fn test_repository_builder_new_without_key() {
let set = InstructionSet::new(vec![]);
let builder = set.for_repository("UserRepo", None);
assert_eq!(builder.entity_name(), "UserRepo");
assert_eq!(builder.key(), None);
}
#[test]
fn test_repository_builder_rename_with_new_key() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_repository("OldRepo", Some("oldKey"));
builder.rename_repository("NewRepo", Some("newKey"));
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::RepositoryRename);
assert_eq!(steps[0].entity_name, Some("OldRepo".to_string()));
}
#[test]
fn test_repository_builder_rename_without_key() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_repository("OldRepo", None);
builder.rename_repository("NewRepo", None);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::RepositoryRename);
}
#[test]
fn test_repository_builder_add_field_with_default() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_repository("UserRepo", Some("id"));
builder.add_field("status", Some(Value::from("active")), None::<fn(Document) -> NitriteResult<Value>>);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::RepositoryAddField);
}
#[test]
fn test_repository_builder_add_field_with_generator() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_repository("UserRepo", Some("id"));
builder.add_field(
"createdAt",
None,
Some(|_doc| Ok(Value::from("2024-01-01"))),
);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::RepositoryAddField);
}
#[test]
fn test_repository_builder_rename_field() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_repository("UserRepo", Some("id"));
builder.rename_field("usr_name", "username");
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::RepositoryRenameField);
}
#[test]
fn test_repository_builder_delete_field() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_repository("UserRepo", Some("id"));
builder.delete_field("deprecatedField");
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::RepositoryDeleteField);
}
#[test]
fn test_repository_builder_change_data_type() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_repository("UserRepo", Some("id"));
builder.change_data_type("age", Ok);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::RepositoryChangeDataType);
}
#[test]
fn test_repository_builder_change_id_field() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_repository("UserRepo", Some("id"));
builder.change_id_field(&["oldId"], &["newId"]);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::RepositoryChangeIdField);
}
#[test]
fn test_repository_builder_change_id_field_multiple() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_repository("UserRepo", Some("id"));
builder.change_id_field(&["firstName", "lastName"], &["fullName"]);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::RepositoryChangeIdField);
}
#[test]
fn test_repository_builder_drop_index() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_repository("UserRepo", Some("id"));
builder.drop_index(&["email"]);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::RepositoryDropIndex);
}
#[test]
fn test_repository_builder_drop_all_indices() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_repository("UserRepo", Some("id"));
builder.drop_all_indices();
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::RepositoryDropAllIndices);
}
#[test]
fn test_repository_builder_create_index() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_repository("UserRepo", Some("id"));
builder.create_index("UNIQUE", &["email"]);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 1);
assert_eq!(steps[0].instruction_type, InstructionType::RepositoryCreateIndex);
}
#[test]
fn test_repository_builder_chained_operations() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_repository("UserRepo", Some("id"));
builder
.add_field("age", Some(Value::from(0)), None::<fn(Document) -> NitriteResult<Value>>)
.rename_field("usr_name", "username")
.create_index("UNIQUE", &["username"])
.delete_field("temp");
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 4);
assert_eq!(steps[0].instruction_type, InstructionType::RepositoryAddField);
assert_eq!(steps[1].instruction_type, InstructionType::RepositoryRenameField);
assert_eq!(steps[2].instruction_type, InstructionType::RepositoryCreateIndex);
assert_eq!(steps[3].instruction_type, InstructionType::RepositoryDeleteField);
}
#[test]
fn test_repository_builder_full_workflow() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_repository("UserRepo", Some("id"));
builder
.rename_repository("ModernUserRepo", Some("userId"))
.add_field("createdAt", Some(Value::from("2024-01-01")), None::<fn(Document) -> NitriteResult<Value>>)
.change_id_field(&["id"], &["userId"])
.create_index("UNIQUE", &["email"])
.change_data_type("age", Ok)
.drop_index(&["oldIndex"]);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 6);
}
#[test]
fn test_instruction_set_shared_state() {
let set = InstructionSet::new(vec![]);
let mut db_builder = set.for_database();
let mut col_builder = set.for_collection("col1");
let mut repo_builder = set.for_repository("Repo1", None);
db_builder.add_user("admin", "pass");
col_builder.add_field("field1", None, None::<fn(Document) -> NitriteResult<Value>>);
repo_builder.add_field("field2", Some(Value::from(1)), None::<fn(Document) -> NitriteResult<Value>>);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 3);
}
#[test]
fn test_multiple_collections_same_set() {
let set = InstructionSet::new(vec![]);
let mut col1 = set.for_collection("users");
let mut col2 = set.for_collection("products");
col1.add_field("email", None, None::<fn(Document) -> NitriteResult<Value>>);
col2.add_field("price", None, None::<fn(Document) -> NitriteResult<Value>>);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 2);
assert_eq!(steps[0].collection_name, Some("users".to_string()));
assert_eq!(steps[1].collection_name, Some("products".to_string()));
}
#[test]
fn test_complex_migration_scenario() {
let set = InstructionSet::new(vec![]);
let mut db = set.for_database();
db.add_user("admin", "secure_pass");
let mut col = set.for_collection("legacy_users");
col.rename("users")
.add_field("version", Some(Value::from(1)), None::<fn(Document) -> NitriteResult<Value>>)
.create_index("UNIQUE", &["email"]);
let mut repo = set.for_repository("OldUserRepo", Some("id"));
repo.rename_repository("UserRepository", Some("userId"))
.change_id_field(&["id"], &["userId"])
.add_field("migrated", Some(Value::from(true)), None::<fn(Document) -> NitriteResult<Value>>);
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 7);
assert_eq!(steps[0].instruction_type, InstructionType::AddUser);
assert_eq!(steps[1].instruction_type, InstructionType::CollectionRename);
assert_eq!(steps[2].instruction_type, InstructionType::AddField);
assert_eq!(steps[3].instruction_type, InstructionType::CreateIndex);
assert_eq!(steps[4].instruction_type, InstructionType::RepositoryRename);
assert_eq!(steps[5].instruction_type, InstructionType::RepositoryChangeIdField);
assert_eq!(steps[6].instruction_type, InstructionType::RepositoryAddField);
}
#[test]
fn test_collection_name_with_special_chars() {
let set = InstructionSet::new(vec![]);
let builder = set.for_collection("user_data_v2.0");
assert_eq!(builder.collection_name(), "user_data_v2.0");
}
#[test]
fn test_repository_entity_name_with_namespace() {
let set = InstructionSet::new(vec![]);
let builder = set.for_repository("com.example.User", Some("userId"));
assert_eq!(builder.entity_name(), "com.example.User");
}
#[test]
fn test_builder_returns_mutable_reference() {
let set = InstructionSet::new(vec![]);
let mut builder = set.for_database();
let returned = builder.add_user("user", "pass");
let _ = returned.add_user("user2", "pass2");
let steps = set.get_steps().unwrap();
assert_eq!(steps.len(), 2);
}
}