use serde::de::DeserializeOwned;
use crate::error::{DecodeError, Error};
use crate::query::Row;
pub trait FromRow: Sized {
fn from_row(row: &Row) -> crate::Result<Self>;
}
impl<T: DeserializeOwned> FromRow for T {
fn from_row(row: &Row) -> crate::Result<Self> {
let map = row.map().clone();
let value = serde_json::Value::Object(map);
serde_json::from_value(value).map_err(|e| Error::Decode {
column: None,
source: DecodeError::Serde(e.to_string()),
})
}
}
#[cfg(test)]
mod tests {
use serde::Deserialize;
use serde_json::json;
use super::*;
#[derive(Debug, Deserialize, PartialEq)]
struct Employee {
#[serde(rename = "EMPNO")]
empno: String,
#[serde(rename = "SALARY")]
salary: f64,
}
#[test]
fn blanket_impl_decodes_a_row() {
let serde_json::Value::Object(map) = json!({ "EMPNO": "000010", "SALARY": 52750.0 }) else {
unreachable!()
};
let row = Row::from_map(map);
let emp = Employee::from_row(&row).expect("FromRow decodes the test row");
assert_eq!(
emp,
Employee {
empno: "000010".into(),
salary: 52750.0,
}
);
}
#[test]
fn blanket_impl_surfaces_decode_error() {
let serde_json::Value::Object(map) = json!({ "EMPNO": "000010" }) else {
unreachable!()
};
let row = Row::from_map(map);
let err = Employee::from_row(&row).expect_err("missing SALARY");
match err {
Error::Decode { column, .. } => assert_eq!(column, None),
other => panic!("expected Error::Decode, got {other:?}"),
}
}
#[test]
fn hand_rolled_from_row_overrides_blanket() {
struct Custom {
empno_int: i64,
full_label: String,
}
impl FromRow for Custom {
fn from_row(row: &Row) -> crate::Result<Self> {
let empno: String = row.get("EMPNO")?;
let salary: f64 = row.get("SALARY")?;
Ok(Custom {
empno_int: empno.parse().unwrap_or_default(),
full_label: format!("emp {empno} @ ${salary}"),
})
}
}
let mut m = serde_json::Map::new();
m.insert("EMPNO".into(), json!("12345"));
m.insert("SALARY".into(), json!(75000.0));
let row = Row::from_map(m);
let c: Custom = Custom::from_row(&row).expect("FromRow decodes the test row");
assert_eq!(c.empno_int, 12345);
assert_eq!(c.full_label, "emp 12345 @ $75000");
}
}