// Copyright 2024 Vincent Chan // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::cmp::Ordering; use std::io::{BufRead, Read, Write}; use bson::{Bson, DateTime, Decimal128, Document, Timestamp}; use bson::oid::ObjectId; use bson::spec::ElementType; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use bson::ser::Error as BsonErr; use bson::ser::Result as BsonResult; use crate::{Error, Result}; pub fn stacked_key<'a, T: IntoIterator>(keys: T) -> Result> { let mut result = Vec::::new(); for key in keys { stacked_key_bytes(&mut result, key)?; } Ok(result) } pub fn stacked_key_bytes(writer: &mut W, key: &Bson) -> Result<()> { match key { Bson::Double(dbl) => { writer.write_u8(ElementType::Double as u8)?; writer.write_f64::(*dbl)?; } Bson::String(str) => { writer.write_u8(ElementType::String as u8)?; writer.write_all(str.as_bytes())?; writer.write_u8(0)?; } Bson::Boolean(bl) => { writer.write_u8(ElementType::Boolean as u8)?; writer.write_u8(*bl as u8)?; } Bson::Null => { writer.write_u8(ElementType::Null as u8)?; } Bson::Int32(i32) => { writer.write_u8(ElementType::Int32 as u8)?; writer.write_i32::(*i32)?; } Bson::Int64(i64) => { writer.write_u8(ElementType::Int64 as u8)?; writer.write_i64::(*i64)?; } Bson::Timestamp(ts) => { writer.write_u8(ElementType::Timestamp as u8)?; let u64 = ((ts.time as u64) << 32) | (ts.increment as u64); writer.write_u64::(u64)?; } Bson::ObjectId(oid) => { writer.write_u8(ElementType::ObjectId as u8)?; let bytes = oid.bytes(); writer.write_all(&bytes)?; } Bson::DateTime(dt) => { writer.write_u8(ElementType::DateTime as u8)?; let t = dt.timestamp_millis(); writer.write_i64::(t)?; } Bson::Symbol(str) => { writer.write_u8(ElementType::Symbol as u8)?; writer.write_all(str.as_bytes())?; writer.write_u8(0)?; } Bson::Decimal128(dcl) => { writer.write_u8(ElementType::Decimal128 as u8)?; let bytes = dcl.bytes(); writer.write_all(&bytes)?; } Bson::Undefined => { writer.write_u8(ElementType::Undefined as u8)?; } _ => { let val = format!("{:?}", key); return Err(Error::NotAValidKeyType(val)) } } Ok(()) } pub fn split_stacked_keys(buffer: &[u8]) -> Result> { let mut result = Vec::::new(); let mut reader = buffer; loop { let ch_result = reader.read_u8(); if ch_result.is_err() { break; } let ch = ch_result.unwrap(); if ch == ElementType::Double as u8 { let val = reader.read_f64::()?; result.push(Bson::Double(val)); } else if ch == ElementType::String as u8 { let mut bytes = Vec::::new(); reader.read_until(0, &mut bytes)?; // remove last byte of bytes bytes.pop(); result.push(Bson::String(String::from_utf8(bytes)?)); } else if ch == ElementType::Boolean as u8 { let val = reader.read_u8()?; result.push(Bson::Boolean(val != 0)); } else if ch == ElementType::Null as u8 { result.push(Bson::Null); } else if ch == ElementType::Int32 as u8 { let val = reader.read_i32::()?; result.push(Bson::Int32(val)); } else if ch == ElementType::Int64 as u8 { let val = reader.read_i64::()?; result.push(Bson::Int64(val)); } else if ch == ElementType::Timestamp as u8 { let val = reader.read_u64::()?; let timestamp = Timestamp { time: (val >> 32) as u32, increment: val as u32, }; result.push(Bson::Timestamp(timestamp)); } else if ch == ElementType::ObjectId as u8 { let mut bytes = [0u8; 12]; reader.read_exact(&mut bytes)?; result.push(Bson::ObjectId(ObjectId::from_bytes(bytes))); } else if ch == ElementType::DateTime as u8 { let val = reader.read_i64::()?; let datetime = DateTime::from_millis(val); result.push(Bson::DateTime(datetime)); } else if ch == ElementType::Symbol as u8 { let mut bytes = Vec::::new(); reader.read_until(0, &mut bytes)?; bytes.pop(); result.push(Bson::Symbol(String::from_utf8(bytes)?)); } else if ch == ElementType::Decimal128 as u8 { let mut bytes = [0u8; 16]; reader.read_exact(&mut bytes)?; result.push(Bson::Decimal128(Decimal128::from_bytes(bytes))); } else if ch == ElementType::Undefined as u8 { result.push(Bson::Undefined); } else { return Err(Error::UnknownBsonElementType(ch)); } } Ok(result) } pub fn value_cmp(a: &Bson, b: &Bson) -> BsonResult { match (a, b) { (Bson::Null, Bson::Null) => Ok(Ordering::Equal), (Bson::Undefined, Bson::Undefined) => Ok(Ordering::Equal), (Bson::DateTime(d1), Bson::DateTime(d2)) => Ok(d1.cmp(d2)), (Bson::Boolean(b1), Bson::Boolean(b2)) => Ok(b1.cmp(b2)), (Bson::Int64(i1), Bson::Int64(i2)) => Ok(i1.cmp(i2)), (Bson::Int32(i1), Bson::Int32(i2)) => Ok(i1.cmp(i2)), (Bson::Int64(i1), Bson::Int32(i2)) => { let i2_64 = *i2 as i64; Ok(i1.cmp(&i2_64)) }, (Bson::Int32(i1), Bson::Int64(i2)) => { let i1_64 = *i1 as i64; Ok(i1_64.cmp(i2)) }, (Bson::Double(d1), Bson::Double(d2)) => Ok(d1.total_cmp(d2)), (Bson::Double(d1), Bson::Int32(d2)) => { let f = *d2 as f64; Ok(d1.total_cmp(&f)) }, (Bson::Double(d1), Bson::Int64(d2)) => { let f = *d2 as f64; Ok(d1.total_cmp(&f)) }, (Bson::Int32(i1), Bson::Double(d2)) => { let f = *i1 as f64; Ok(f.total_cmp(d2)) } (Bson::Int64(i1), Bson::Double(d2)) => { let f = *i1 as f64; Ok(f.total_cmp(d2)) } (Bson::Binary(b1), Bson::Binary(b2)) => Ok(b1.bytes.cmp(&b2.bytes)), (Bson::String(str1), Bson::String(str2)) => Ok(str1.cmp(str2)), (Bson::ObjectId(oid1), Bson::ObjectId(oid2)) => Ok(oid1.cmp(oid2)), _ => { // compare the numeric type let a_type = a.element_type() as u8; let b_type = b.element_type() as u8; if a_type != b_type { return Ok(a_type.cmp(&b_type)); } Err(BsonErr::InvalidCString("Unsupported types".to_string())) }, } } pub fn try_get_document_value(doc: &Document, key: &str) -> Option { let keys = key.split('.').collect::>(); let keys_slice = keys.as_slice(); try_get_document_by_slices(doc, keys_slice) } fn try_get_document_by_slices(doc: &Document, keys: &[&str]) -> Option { let first = keys.first(); first .and_then(|first_str| { let remains = &keys[1..]; let value = doc.get(first_str); match value { Some(Bson::Document(doc)) => { try_get_document_by_slices(doc, remains) } Some(v) => { if remains.is_empty() { return Some(v.clone()); } None } _ => None, } }) } pub fn bson_datetime_now() -> DateTime { DateTime::now() } #[cfg(test)] mod tests { use std::cmp::Ordering; use bson::{Bson, doc, Timestamp}; use bson::oid::ObjectId; use crate::utils::bson::{split_stacked_keys, stacked_key, value_cmp}; #[test] fn test_value_cmp() { assert_eq!(value_cmp(&Bson::Int32(2), &Bson::Int64(3)).unwrap(), Ordering::Less); assert_eq!(value_cmp(&Bson::Int32(2), &Bson::Int64(1)).unwrap(), Ordering::Greater); assert_eq!(value_cmp(&Bson::Int32(1), &Bson::Int64(1)).unwrap(), Ordering::Equal); assert_eq!(value_cmp(&Bson::Int64(2), &Bson::Int32(3)).unwrap(), Ordering::Less); assert_eq!(value_cmp(&Bson::Int64(2), &Bson::Int32(1)).unwrap(), Ordering::Greater); assert_eq!(value_cmp(&Bson::Int64(1), &Bson::Int32(1)).unwrap(), Ordering::Equal); } #[test] fn test_try_get_document_value() { assert_eq!(super::try_get_document_value(&doc!{}, "a"), None); assert_eq!(super::try_get_document_value(&doc!{"a": 1}, "a"), Some(Bson::Int32(1))); assert_eq!(super::try_get_document_value(&doc!{"a": 1}, "b"), None); assert_eq!(super::try_get_document_value(&doc!{"a": { "b": 1 }}, "a.b"), Some(Bson::Int32(1))); assert_eq!(super::try_get_document_value(&doc!{"a": { "b": 1 }}, "a.c"), None); assert_eq!(super::try_get_document_value(&doc!{"a": { "b": { "c": 1 }}}, "a.b.c"), Some(Bson::Int32(1))); assert_eq!(super::try_get_document_value(&doc!{"a": { "b": { "c": 1 }}}, "a.b.d"), None); } #[test] fn test_split_stacked_keys() { let values = vec![ Bson::ObjectId(ObjectId::new()), Bson::String("Hello".to_string()), Bson::Int32(42), Bson::Int64(42), Bson::Double(3.14), Bson::Undefined, Bson::Null, Bson::Boolean(true), Bson::Timestamp(Timestamp { time: 42, increment: 42 }), Bson::DateTime(super::bson_datetime_now()), ]; let stacked = stacked_key(&values).unwrap(); let slices = split_stacked_keys(&stacked).unwrap(); // deep compare slices and values assert_eq!(slices.len(), values.len()); for i in 0..slices.len() { assert_eq!(slices[i], values[i]); } } }