I've got this code that matches a type coming from an unknown sqlx query. In the example, it's PgRow and I want to transform it into an enum MyDynamicValue depending on the type_info. I could implement it for every type defined by every databases and add an entry in the match. I think that you can see that it's a lot repetitive.
fn get_row_value(row: &PgRow, col_index: usize) -> Result<Option<MyDynamicValue>> {
let value_ref = row
.try_get_raw(col_index)
.map_err(|err| Error::RunQueryError)?;
Ok(match value_ref.type_info().name() {
"VARCHAR" => row
.try_get::<Option<String>, usize>(col_index)
.map_err(|err| Error::RunQueryError)?
.map(|value| MyDynamicValue::String(value)),
"INT4" => row
.try_get::<Option<i32>, usize>(col_index)
.map_err(|err| Error::RunQueryError)?
.map(|value| MyDynamicValue::Integer(value)),
"BYTEA" => row
.try_get::<Option<&[u8]>, usize>(col_index)
.map_err(|err| Error::RunQueryError)?
.map(|value| MyDynamicValue::Bytea(value.to_owned())),
// ...
name => panic!("UNHANDLED DATABASE TYPE: {name}"),
})
}
#[derive(PartialEq, Debug, Clone)]
pub enum MyDynamicValue {
String(String),
Integer(i32),
Bytea(Vec<u8>),
}
The parts that I want to refactor is:
"VARCHAR" => row
.try_get::<Option<String>, usize>(col_index)
.map_err(|err| Error::RunQueryError)?
.map(|value| MyDynamicValue::String(value)),
And I would like to only write:
row.try_get(col_index).into()
The problem is that I match on type_info.name() to know the type of the output.
Anyway, I'm not sure about good pratices and if someone can lighten the path, I'll gladly follow.
I tried a few things always lead to me realizing I was in over my head.
I tried implementing From<dyn Decode<'a, T, DB>>: compiler was not happy.
How can I make this code less repetitive?
Bonus question: How can I even do it for any database type?