#![allow(
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_sign_loss
)]
use tantivy::schema::{NamedFieldDocument, OwnedValue, Value as _};
use crate::{ParseError, ToValueType};
impl<'a> ToValueType<&'a str> for &'a OwnedValue {
fn to_value_type(self) -> Result<&'a str, ParseError> {
self.as_str()
.ok_or_else(|| ParseError::ConvertType("&str".into()))
}
}
impl<'a> ToValueType<&'a OwnedValue> for &'a OwnedValue {
fn to_value_type(self) -> Result<&'a OwnedValue, ParseError> {
Ok(self)
}
}
impl<'a, T> ToValueType<Option<T>> for &'a OwnedValue
where
&'a OwnedValue: ToValueType<T>,
{
fn to_value_type(self) -> Result<Option<T>, ParseError> {
self.to_value_type().map(|inner| Some(inner))
}
fn missing_value(&self, _error: ParseError) -> Result<Option<T>, ParseError> {
Ok(None)
}
}
impl ToValueType<String> for &OwnedValue {
fn to_value_type(self) -> Result<String, ParseError> {
Ok(self
.as_str()
.ok_or_else(|| ParseError::ConvertType("String".into()))?
.to_string())
}
}
impl ToValueType<bool> for &OwnedValue {
fn to_value_type(self) -> Result<bool, ParseError> {
self.as_bool()
.ok_or_else(|| ParseError::ConvertType("bool".into()))
}
}
impl ToValueType<f32> for &OwnedValue {
fn to_value_type(self) -> Result<f32, ParseError> {
Ok(self
.as_f64()
.ok_or_else(|| ParseError::ConvertType("f32".into()))? as f32)
}
}
impl ToValueType<f64> for &OwnedValue {
fn to_value_type(self) -> Result<f64, ParseError> {
self.as_f64()
.ok_or_else(|| ParseError::ConvertType("f64".into()))
}
}
impl ToValueType<u8> for &OwnedValue {
fn to_value_type(self) -> Result<u8, ParseError> {
Ok(self
.as_u64()
.ok_or_else(|| ParseError::ConvertType("u8".into()))? as u8)
}
}
impl ToValueType<u16> for &OwnedValue {
fn to_value_type(self) -> Result<u16, ParseError> {
Ok(self
.as_u64()
.ok_or_else(|| ParseError::ConvertType("u16".into()))? as u16)
}
}
impl ToValueType<u32> for &OwnedValue {
fn to_value_type(self) -> Result<u32, ParseError> {
Ok(self
.as_u64()
.ok_or_else(|| ParseError::ConvertType("u32".into()))? as u32)
}
}
impl ToValueType<u64> for &OwnedValue {
fn to_value_type(self) -> Result<u64, ParseError> {
self.as_u64()
.ok_or_else(|| ParseError::ConvertType("u64".into()))
}
}
pub trait ToValue<Type> {
fn to_value<'a, T>(&'a self, index: &str) -> Result<T, ParseError>
where
Type: 'a,
&'a Type: ToValueType<T>;
}
impl ToValue<Vec<OwnedValue>> for NamedFieldDocument {
fn to_value<'a, T>(&'a self, index: &str) -> Result<T, ParseError>
where
Vec<OwnedValue>: 'a,
&'a Vec<OwnedValue>: ToValueType<T>,
{
get_doc_value_types(self, index)
}
}
impl ToValue<Vec<OwnedValue>> for &NamedFieldDocument {
fn to_value<'a, T>(&'a self, index: &str) -> Result<T, ParseError>
where
Vec<OwnedValue>: 'a,
&'a Vec<OwnedValue>: ToValueType<T>,
{
get_doc_value_types(self, index)
}
}
pub fn get_doc_value_types<'a, T>(
value: &'a NamedFieldDocument,
index: &str,
) -> Result<T, ParseError>
where
&'a Vec<OwnedValue>: ToValueType<T>,
{
if let Some(inner) = value.0.get(index) {
return inner.to_value_type();
}
Err(ParseError::Parse(format!("Missing value: '{index}'")))
}
pub fn get_value_type<'a, T>(value: &'a NamedFieldDocument, index: &str) -> Result<T, ParseError>
where
&'a OwnedValue: ToValueType<T>,
{
if let Some(inner) = value.0.get(index)
&& let Some(inner) = inner.first()
{
let inner = inner.to_value_type()?;
return Ok(inner);
}
Err(ParseError::Parse(format!("Missing value: '{index}'")))
}
impl<'a> ToValueType<&'a Vec<OwnedValue>> for &'a Vec<OwnedValue> {
fn to_value_type(self) -> Result<&'a Vec<OwnedValue>, ParseError> {
Ok(self)
}
}
impl<'a, T> ToValueType<Vec<T>> for &'a Vec<OwnedValue>
where
&'a OwnedValue: ToValueType<T>,
{
fn to_value_type(self) -> Result<Vec<T>, ParseError> {
self.iter()
.map(ToValueType::to_value_type)
.collect::<Result<Vec<_>, _>>()
}
}
impl<'a, T> ToValueType<T> for &'a Vec<OwnedValue>
where
&'a OwnedValue: ToValueType<T>,
{
fn to_value_type(self) -> Result<T, ParseError> {
self.first()
.map(ToValueType::to_value_type)
.ok_or_else(|| ParseError::ConvertType("&str".into()))?
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test_log::test]
fn test_to_value_type_str() {
let value = OwnedValue::Str("test".to_string());
let result: Result<&str, ParseError> = (&value).to_value_type();
assert_eq!(result.unwrap(), "test");
}
#[test_log::test]
fn test_to_value_type_string() {
let value = OwnedValue::Str("test".to_string());
let result: Result<String, ParseError> = (&value).to_value_type();
assert_eq!(result.unwrap(), "test");
}
#[test_log::test]
fn test_to_value_type_bool() {
let value = OwnedValue::Bool(true);
let result: Result<bool, ParseError> = (&value).to_value_type();
assert!(result.unwrap());
let value = OwnedValue::Bool(false);
let result: Result<bool, ParseError> = (&value).to_value_type();
assert!(!result.unwrap());
}
#[test_log::test]
fn test_to_value_type_u64() {
let value = OwnedValue::U64(123);
let result: Result<u64, ParseError> = (&value).to_value_type();
assert_eq!(result.unwrap(), 123);
}
#[test_log::test]
fn test_to_value_type_f64() {
let value = OwnedValue::F64(2.5);
let result: Result<f64, ParseError> = (&value).to_value_type();
assert!((result.unwrap() - 2.5).abs() < f64::EPSILON);
}
#[test_log::test]
fn test_to_value_type_f32() {
let value = OwnedValue::F64(2.5);
let result: Result<f32, ParseError> = (&value).to_value_type();
assert!((result.unwrap() - 2.5_f32).abs() < 0.001);
}
#[test_log::test]
fn test_to_value_type_u8() {
let value = OwnedValue::U64(42);
let result: Result<u8, ParseError> = (&value).to_value_type();
assert_eq!(result.unwrap(), 42);
}
#[test_log::test]
fn test_to_value_type_u16() {
let value = OwnedValue::U64(1234);
let result: Result<u16, ParseError> = (&value).to_value_type();
assert_eq!(result.unwrap(), 1234);
}
#[test_log::test]
fn test_to_value_type_u32() {
let value = OwnedValue::U64(12345);
let result: Result<u32, ParseError> = (&value).to_value_type();
assert_eq!(result.unwrap(), 12345);
}
#[test_log::test]
fn test_to_value_type_option() {
let value = OwnedValue::Str("test".to_string());
let result: Result<Option<String>, ParseError> = (&value).to_value_type();
assert_eq!(result.unwrap(), Some("test".to_string()));
}
#[test_log::test]
fn test_to_value_type_owned_value_identity() {
let value = OwnedValue::U64(123);
let result: Result<&OwnedValue, ParseError> = (&value).to_value_type();
assert!(result.is_ok());
}
#[test_log::test]
fn test_vec_owned_value_to_vec() {
let values = vec![OwnedValue::U64(1), OwnedValue::U64(2), OwnedValue::U64(3)];
let result: Result<Vec<u64>, ParseError> = (&values).to_value_type();
assert_eq!(result.unwrap(), vec![1, 2, 3]);
}
#[test_log::test]
fn test_vec_owned_value_to_first_value() {
let values = vec![OwnedValue::U64(42)];
let result: Result<u64, ParseError> = (&values).to_value_type();
assert_eq!(result.unwrap(), 42);
}
#[test_log::test]
fn test_vec_owned_value_empty_error() {
let values: Vec<OwnedValue> = vec![];
let result: Result<u64, ParseError> = (&values).to_value_type();
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), ParseError::ConvertType(_)));
}
#[test_log::test]
fn test_vec_owned_value_identity() {
let values = vec![OwnedValue::U64(1), OwnedValue::U64(2)];
let result: Result<&Vec<OwnedValue>, ParseError> = (&values).to_value_type();
assert!(result.is_ok());
assert_eq!(result.unwrap().len(), 2);
}
#[test_log::test]
fn test_get_value_type_from_document() {
use std::collections::BTreeMap;
let mut fields = BTreeMap::new();
fields.insert(
"test_field".to_string(),
vec![OwnedValue::Str("test_value".to_string())],
);
let doc = NamedFieldDocument(fields);
let result: Result<String, ParseError> = get_value_type(&doc, "test_field");
assert_eq!(result.unwrap(), "test_value");
}
#[test_log::test]
fn test_get_value_type_missing_field() {
use std::collections::BTreeMap;
let fields = BTreeMap::new();
let doc = NamedFieldDocument(fields);
let result: Result<String, ParseError> = get_value_type(&doc, "missing_field");
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), ParseError::Parse(_)));
}
#[test_log::test]
fn test_get_doc_value_types() {
use std::collections::BTreeMap;
let mut fields = BTreeMap::new();
fields.insert(
"numbers".to_string(),
vec![OwnedValue::U64(1), OwnedValue::U64(2), OwnedValue::U64(3)],
);
let doc = NamedFieldDocument(fields);
let result: Result<Vec<u64>, ParseError> = get_doc_value_types(&doc, "numbers");
assert_eq!(result.unwrap(), vec![1, 2, 3]);
}
#[test_log::test]
fn test_to_value_trait_on_document() {
use std::collections::BTreeMap;
let mut fields = BTreeMap::new();
fields.insert(
"test".to_string(),
vec![OwnedValue::Str("value".to_string())],
);
let doc = NamedFieldDocument(fields);
let result: Result<String, ParseError> = doc.to_value("test");
assert_eq!(result.unwrap(), "value");
}
#[test_log::test]
fn test_to_value_trait_on_document_ref() {
use std::collections::BTreeMap;
let mut fields = BTreeMap::new();
fields.insert("num".to_string(), vec![OwnedValue::U64(42)]);
let doc = NamedFieldDocument(fields);
let result: Result<u64, ParseError> = doc.to_value("num");
assert_eq!(result.unwrap(), 42);
}
#[test_log::test]
fn test_option_missing_value_returns_none() {
let value = OwnedValue::U64(42);
let result = <&OwnedValue as ToValueType<Option<String>>>::missing_value(
&&value,
ParseError::Parse("test".to_string()),
);
assert_eq!(result.unwrap(), None);
}
#[test_log::test]
fn test_to_value_type_str_error_on_wrong_type() {
let value = OwnedValue::U64(42);
let result: Result<&str, ParseError> = (&value).to_value_type();
assert!(matches!(result.unwrap_err(), ParseError::ConvertType(_)));
}
#[test_log::test]
fn test_to_value_type_string_error_on_wrong_type() {
let value = OwnedValue::U64(42);
let result: Result<String, ParseError> = (&value).to_value_type();
assert!(matches!(result.unwrap_err(), ParseError::ConvertType(_)));
}
#[test_log::test]
fn test_to_value_type_bool_error_on_wrong_type() {
let value = OwnedValue::Str("true".to_string());
let result: Result<bool, ParseError> = (&value).to_value_type();
assert!(matches!(result.unwrap_err(), ParseError::ConvertType(_)));
}
#[test_log::test]
fn test_to_value_type_u64_error_on_wrong_type() {
let value = OwnedValue::Str("123".to_string());
let result: Result<u64, ParseError> = (&value).to_value_type();
assert!(matches!(result.unwrap_err(), ParseError::ConvertType(_)));
}
#[test_log::test]
fn test_to_value_type_f64_error_on_wrong_type() {
let value = OwnedValue::Str("2.5".to_string());
let result: Result<f64, ParseError> = (&value).to_value_type();
assert!(matches!(result.unwrap_err(), ParseError::ConvertType(_)));
}
#[test_log::test]
fn test_get_doc_value_types_missing_field() {
use std::collections::BTreeMap;
let fields = BTreeMap::new();
let doc = NamedFieldDocument(fields);
let result: Result<Vec<u64>, ParseError> = get_doc_value_types(&doc, "missing");
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), ParseError::Parse(_)));
}
#[test_log::test]
fn test_vec_conversion_with_element_error() {
let values = vec![
OwnedValue::U64(1),
OwnedValue::Str("not a number".to_string()),
OwnedValue::U64(3),
];
let result: Result<Vec<u64>, ParseError> = (&values).to_value_type();
assert!(result.is_err());
}
#[test_log::test]
fn test_to_value_missing_field_error() {
use std::collections::BTreeMap;
let mut fields = BTreeMap::new();
fields.insert("existing".to_string(), vec![OwnedValue::U64(42)]);
let doc = NamedFieldDocument(fields);
let result: Result<Vec<u64>, ParseError> = doc.to_value("nonexistent");
assert!(result.is_err());
let err = result.unwrap_err();
assert!(matches!(err, ParseError::Parse(_)));
assert!(err.to_string().contains("nonexistent"));
}
#[test_log::test]
fn test_to_value_with_doc_ref() {
use std::collections::BTreeMap;
let mut fields = BTreeMap::new();
fields.insert("field".to_string(), vec![OwnedValue::U64(99)]);
let doc = NamedFieldDocument(fields);
let doc_ref = &doc;
let result: Result<u64, ParseError> = doc_ref.to_value("field");
assert_eq!(result.unwrap(), 99);
}
#[test_log::test]
fn test_get_value_type_empty_field() {
use std::collections::BTreeMap;
let mut fields = BTreeMap::new();
fields.insert("empty_field".to_string(), vec![]);
let doc = NamedFieldDocument(fields);
let result: Result<String, ParseError> = get_value_type(&doc, "empty_field");
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), ParseError::Parse(_)));
}
}