use mongodb::bson::Document;
use crate::error::{ParseError, Result};
use crate::parser::command::{AdminCommand, Command};
use crate::parser::mongo_ast::*;
use super::args::ArgParser;
pub struct AdminOpsParser;
impl AdminOpsParser {
pub fn parse_get_indexes(collection: &str) -> Result<Command> {
Ok(Command::Admin(AdminCommand::ListIndexes(
collection.to_string(),
)))
}
pub fn parse_create_index(collection: &str, args: &[Expr]) -> Result<Command> {
let keys = ArgParser::get_doc_arg(args, 0)?;
let options = if args.len() > 1 {
Some(ArgParser::get_doc_arg(args, 1)?)
} else {
None
};
let mut index_spec = Document::new();
index_spec.insert("key", keys);
if let Some(opts) = options {
for (key, value) in opts {
index_spec.insert(key, value);
}
}
Ok(Command::Admin(AdminCommand::CreateIndexes {
collection: collection.to_string(),
indexes: vec![index_spec],
}))
}
pub fn parse_create_indexes(collection: &str, args: &[Expr]) -> Result<Command> {
let indexes = ArgParser::get_doc_array_arg(args, 0)?;
Ok(Command::Admin(AdminCommand::CreateIndexes {
collection: collection.to_string(),
indexes,
}))
}
pub fn parse_drop_index(collection: &str, args: &[Expr]) -> Result<Command> {
let index = ArgParser::get_string_arg(args, 0)?;
Ok(Command::Admin(AdminCommand::DropIndex {
collection: collection.to_string(),
index,
}))
}
pub fn parse_drop_indexes(collection: &str) -> Result<Command> {
Ok(Command::Admin(AdminCommand::DropIndex {
collection: collection.to_string(),
index: "*".to_string(),
}))
}
pub fn parse_drop_collection(collection: &str) -> Result<Command> {
Ok(Command::Admin(AdminCommand::DropCollection(
collection.to_string(),
)))
}
pub fn parse_rename_collection(collection: &str, args: &[Expr]) -> Result<Command> {
if args.is_empty() {
return Err(ParseError::InvalidCommand(
"renameCollection() requires at least 1 argument: target name".to_string(),
)
.into());
}
if args.len() > 2 {
return Err(ParseError::InvalidCommand(
format!("renameCollection() expects at most 2 arguments, got {}", args.len()),
)
.into());
}
let target = match &args[0] {
Expr::String(s) => s.clone(),
_ => {
return Err(ParseError::InvalidCommand(
"renameCollection() target must be a string".to_string(),
)
.into());
}
};
let drop_target = if args.len() > 1 {
match &args[1] {
Expr::Boolean(b) => *b,
_ => {
return Err(ParseError::InvalidCommand(
"renameCollection() dropTarget must be a boolean".to_string(),
)
.into());
}
}
} else {
false
};
Ok(Command::Admin(AdminCommand::RenameCollection {
collection: collection.to_string(),
target,
drop_target,
}))
}
pub fn parse_collection_stats(collection: &str, args: &[Expr]) -> Result<Command> {
let scale = if args.is_empty() {
None
} else if args.len() == 1 {
match &args[0] {
Expr::Number(n) => Some(*n as i32),
Expr::Object(_) => {
let options_doc = ArgParser::get_doc_arg(args, 0)?;
options_doc
.get_i32("scale")
.ok()
.or_else(|| options_doc.get_i64("scale").ok().map(|v| v as i32))
}
_ => {
return Err(ParseError::InvalidCommand(
"stats() argument must be a number or options document".to_string(),
)
.into());
}
}
} else {
return Err(ParseError::InvalidCommand(
format!("stats() expects at most 1 argument, got {}", args.len()),
)
.into());
};
Ok(Command::Admin(AdminCommand::CollectionStats {
collection: collection.to_string(),
scale,
}))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::mongo_operation::DbOperationParser;
use crate::parser::command::AdminCommand;
#[test]
fn test_parse_get_indexes() {
let result = DbOperationParser::parse("db.users.getIndexes()");
assert!(result.is_ok());
}
#[test]
fn test_parse_create_index() {
let result = DbOperationParser::parse("db.users.createIndex({ email: 1 })");
assert!(result.is_ok());
if let Ok(Command::Admin(cmd)) = result {
assert!(matches!(cmd, AdminCommand::CreateIndexes { .. }));
}
}
#[test]
fn test_parse_create_index_with_options() {
let result = DbOperationParser::parse(
"db.users.createIndex({ email: 1 }, { unique: true, name: 'email_idx' })"
);
assert!(result.is_ok());
if let Ok(Command::Admin(AdminCommand::CreateIndexes { indexes, .. })) = result {
assert_eq!(indexes.len(), 1);
assert!(indexes[0].contains_key("unique"));
}
}
#[test]
fn test_parse_create_indexes() {
let result = DbOperationParser::parse("db.users.createIndexes([{ key: { name: 1 } }])");
assert!(result.is_ok());
}
#[test]
fn test_parse_drop_index() {
let result = DbOperationParser::parse("db.users.dropIndex('email_1')");
assert!(result.is_ok());
}
#[test]
fn test_parse_drop_indexes() {
let result = DbOperationParser::parse("db.users.dropIndexes()");
assert!(result.is_ok());
}
#[test]
fn test_parse_drop_collection() {
let result = DbOperationParser::parse("db.users.drop()");
assert!(result.is_ok());
}
#[test]
fn test_parse_rename_collection() {
let result = DbOperationParser::parse("db.users.renameCollection('customers')");
assert!(result.is_ok());
if let Ok(Command::Admin(AdminCommand::RenameCollection { target, drop_target, .. })) = result {
assert_eq!(target, "customers");
assert!(!drop_target);
}
}
#[test]
fn test_parse_rename_collection_with_drop_target() {
let result = DbOperationParser::parse("db.users.renameCollection('customers', true)");
assert!(result.is_ok());
if let Ok(Command::Admin(AdminCommand::RenameCollection { drop_target, .. })) = result {
assert!(drop_target);
}
}
#[test]
fn test_parse_rename_collection_with_drop_target_false() {
let result = DbOperationParser::parse("db.users.renameCollection('customers', false)");
assert!(result.is_ok());
if let Ok(Command::Admin(AdminCommand::RenameCollection { drop_target, .. })) = result {
assert!(!drop_target);
}
}
#[test]
fn test_parse_collection_stats_no_args() {
let result = DbOperationParser::parse("db.users.stats()");
assert!(result.is_ok());
}
#[test]
fn test_parse_collection_stats_with_scale_number() {
let result = DbOperationParser::parse("db.users.stats(1024)");
assert!(result.is_ok());
}
#[test]
fn test_parse_collection_stats_with_options() {
let result = DbOperationParser::parse("db.users.stats({ scale: 1024 })");
assert!(result.is_ok());
}
}