use anyhow::Result;
use drasi_core::models::ElementValue;
use std::sync::Arc;
use tiberius::Row;
pub fn extract_column_value(row: &Row, col_idx: usize) -> Result<ElementValue> {
use tiberius::ColumnType;
let column = &row.columns()[col_idx];
match column.column_type() {
ColumnType::Bit | ColumnType::Bitn => {
if let Ok(Some(val)) = row.try_get::<bool, _>(col_idx) {
Ok(ElementValue::Bool(val))
} else {
Ok(ElementValue::Null)
}
}
ColumnType::Int1
| ColumnType::Int2
| ColumnType::Int4
| ColumnType::Int8
| ColumnType::Intn => {
if let Ok(Some(val)) = row.try_get::<i32, _>(col_idx) {
Ok(ElementValue::Integer(val as i64))
} else if let Ok(Some(val)) = row.try_get::<i64, _>(col_idx) {
Ok(ElementValue::Integer(val))
} else if let Ok(Some(val)) = row.try_get::<i16, _>(col_idx) {
Ok(ElementValue::Integer(val as i64))
} else if let Ok(Some(val)) = row.try_get::<u8, _>(col_idx) {
Ok(ElementValue::Integer(val as i64))
} else {
Ok(ElementValue::Null)
}
}
ColumnType::Float4 | ColumnType::Float8 | ColumnType::Floatn => {
if let Ok(Some(val)) = row.try_get::<f32, _>(col_idx) {
Ok(ElementValue::Float(ordered_float::OrderedFloat(val as f64)))
} else if let Ok(Some(val)) = row.try_get::<f64, _>(col_idx) {
Ok(ElementValue::Float(ordered_float::OrderedFloat(val)))
} else {
Ok(ElementValue::Null)
}
}
ColumnType::Numericn | ColumnType::Decimaln => {
if let Ok(Some(d)) = row.try_get::<rust_decimal::Decimal, _>(col_idx) {
let f = d.to_string().parse::<f64>().unwrap_or(0.0);
Ok(ElementValue::Float(ordered_float::OrderedFloat(f)))
} else {
Ok(ElementValue::Null)
}
}
ColumnType::BigVarChar
| ColumnType::BigChar
| ColumnType::NVarchar
| ColumnType::NChar
| ColumnType::Text
| ColumnType::NText => {
if let Ok(Some(val)) = row.try_get::<&str, _>(col_idx) {
Ok(ElementValue::String(Arc::from(val)))
} else {
Ok(ElementValue::Null)
}
}
ColumnType::Guid => {
if let Ok(Some(val)) = row.try_get::<uuid::Uuid, _>(col_idx) {
Ok(ElementValue::String(Arc::from(val.to_string())))
} else {
Ok(ElementValue::Null)
}
}
ColumnType::Datetime
| ColumnType::Datetime2
| ColumnType::Datetime4
| ColumnType::Datetimen
| ColumnType::Daten => {
if let Ok(Some(val)) = row.try_get::<chrono::NaiveDateTime, _>(col_idx) {
Ok(ElementValue::String(Arc::from(
val.format("%Y-%m-%dT%H:%M:%S%.3f").to_string(),
)))
} else if let Ok(Some(val)) = row.try_get::<chrono::DateTime<chrono::Utc>, _>(col_idx) {
Ok(ElementValue::String(Arc::from(val.to_rfc3339())))
} else {
Ok(ElementValue::Null)
}
}
ColumnType::BigVarBin | ColumnType::BigBinary | ColumnType::Image => {
if let Ok(Some(bytes)) = row.try_get::<&[u8], _>(col_idx) {
Ok(ElementValue::String(Arc::from(format!(
"0x{}",
hex::encode(bytes)
))))
} else {
Ok(ElementValue::Null)
}
}
_ => {
if let Ok(Some(val)) = row.try_get::<&str, _>(col_idx) {
Ok(ElementValue::String(Arc::from(val)))
} else {
log::warn!(
"Unsupported column type {:?} for column {}, treating as NULL",
column.column_type(),
column.name()
);
Ok(ElementValue::Null)
}
}
}
}
pub fn value_to_string(value: &ElementValue) -> String {
match value {
ElementValue::Integer(i) => i.to_string(),
ElementValue::Float(f) => f.to_string(),
ElementValue::String(s) => s.to_string(),
ElementValue::Bool(b) => b.to_string(),
ElementValue::Null => "null".to_string(),
_ => format!("{value:?}"),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_value_to_string() {
assert_eq!(value_to_string(&ElementValue::Integer(42)), "42");
assert_eq!(
value_to_string(&ElementValue::String(Arc::from("test"))),
"test"
);
assert_eq!(value_to_string(&ElementValue::Bool(true)), "true");
assert_eq!(value_to_string(&ElementValue::Null), "null");
}
#[test]
fn test_value_to_string_float() {
let val = ElementValue::Float(ordered_float::OrderedFloat(2.14));
assert_eq!(value_to_string(&val), "2.14");
}
}