notmongo 0.1.7

In-process, bad database with an API similar to MongoDB
Documentation
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();

        // Case: Single field name
        if let Bson::String(field_name) = sort {
            return Ok(Sort::new_single_field(field_name, true));
        }

        // Case: Array of pairs [fieldName, 1/-1]
        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);
        }
        // Case: The sort is invalid
        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 {
        // Compare the fields in order. If any of them are ordered, that is the result
        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);

            // Compare the values
            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 sorting descending, invert the order
            if matches!(order, SingleOrder::Descending) {
                local_order = local_order.reverse();
            }

            // If the values are ordered, that is the result
            if !matches!(local_order, std::cmp::Ordering::Equal) {
                return local_order;
            }
        }

        // None of the fields have yielded any difference, the values are equal
        std::cmp::Ordering::Equal
    }
}