use crate::helpers;
use bson;
use bson::{Bson, Document};
enum SingleOrder {
Ascending,
Descending,
}
pub struct Sort {
field_orders: Vec<(String, SingleOrder)>,
}
impl Sort {
pub fn new_unsorted() -> Sort {
Sort {
field_orders: Vec::new(),
}
}
pub fn new_single_field(field: &str, ascending: bool) -> Sort {
Sort {
field_orders: vec![(
field.to_string(),
if ascending {
SingleOrder::Ascending
} else {
SingleOrder::Descending
},
)],
}
}
pub fn parse_bson<B: Into<Bson>>(sort: B) -> Result<Sort, String> {
let sort: &Bson = &sort.into();
if let Bson::String(field_name) = sort {
return Ok(Sort::new_single_field(field_name, true));
}
if let Bson::Array(sort) = sort {
let mut result = Sort::new_unsorted();
for entry in sort {
let entry = match entry {
Bson::Array(entry) => entry,
_ => {
return Err(
"Elements in the `sort` array need to be pairs [fieldName, 1/-1]"
.to_string(),
)
}
};
if entry.len() != 2 {
return Err(
"Elements in the `sort` array need to be pairs [fieldName, 1/-1]"
.to_string(),
);
}
let field_name = match &entry[0] {
Bson::String(field_name) => field_name,
_ => {
return Err(
"Elements in the `sort` array need to be pairs [fieldName, 1/-1]"
.to_string(),
)
}
};
let sort_int = &entry[1];
let sort_int: i64 = match sort_int {
Bson::Int32(value) => *value as i64,
Bson::Int64(value) => *value,
_ => {
return Err(
"The values in sort objects need to be either -1 or 1".to_string()
)
}
};
let order = match sort_int {
1 => SingleOrder::Ascending,
-1 => SingleOrder::Descending,
value => {
return Err(format!(
"The values in sort objects need to be either -1 or 1, not {}",
value
))
}
};
result.field_orders.push((field_name.clone(), order));
}
return Ok(result);
}
Err("Sort orders need to be either a single field name or Arrays of pairs [fieldName, +1/-1].".to_string())
}
pub(crate) fn is_no_op(&self) -> bool {
self.field_orders.is_empty()
}
pub(crate) fn compare(&self, lhs: &Document, rhs: &Document) -> std::cmp::Ordering {
for (field_name, order) in self.field_orders.iter() {
let lhs_value = helpers::get_document_field_honor_dot_notation(lhs, field_name);
let rhs_value = helpers::get_document_field_honor_dot_notation(rhs, field_name);
let mut local_order = match (lhs_value, rhs_value) {
(Some(lhs), Some(rhs)) => helpers::compare_bson(lhs, rhs),
(None, _) => std::cmp::Ordering::Equal,
(_, None) => std::cmp::Ordering::Equal,
};
if matches!(order, SingleOrder::Descending) {
local_order = local_order.reverse();
}
if !matches!(local_order, std::cmp::Ordering::Equal) {
return local_order;
}
}
std::cmp::Ordering::Equal
}
}