Closed
Description
Having to specify the custom error type in the Database
brings a drawback: it is not possible to have multiple methods using different custom error.
This is typically an issue when wrapping the database to abstract it1, consider the following:
struct WrapperDatabase( Database<anyhow::Error>); // The error is settled here...
struct WrapperTransaction(Transaction<anyhow::Error>);
impl WrapperTransaction {
// Implement business-specific methods
}
impl WrapperDatabase {
async fn for_update<R, E>(
&mut self,
cb: impl AsyncFnOnce(WrapperTransaction) -> Result<R, E>, // ...but we want to be generic over the error type !
) -> anyhow::Result<Result<R, E>> {
self.0.transaction(&["store"])
.rw()
.run(async move |transaction| {
let wrapper = WrapperTransaction(transaction);
cb(updater).await // Error ! expected Result<Result<R, E>, Error<Error>>, found Result<R, E>
})
.await
.map_err(|e| anyhow::anyhow!("{e:?}"))
}
}
A solution to overcome this issue is to rely on type erasing:
struct WrapperDatabase( Database<Box<dyn std::any::Any>>);
struct WrapperTransaction(Transaction<Box<dyn std::any::Any>>);
impl WrapperDatabase {
async fn for_update<R, E>(
&mut self,
cb: impl AsyncFnOnce(WrapperTransaction) -> Result<R, E>,
) -> anyhow::Result<Result<R, E>> {
let outcome = self.0.transaction(&["store"])
.rw()
.run(async move |transaction| {
let wrapper = WrapperTransaction(transaction);
cb(wrapper)
.await
.map_err(|e| {
let boxed = Box::new(e) as Box<dyn std::any::Any>;
boxed.into()
})
})
.await;
match outcome {
Ok(ok) => Ok(Ok(ok)),
Err(err) => match err {
indexed_db::Error::User(err) => Ok(Err(err.downcast().unwrap())),
err => Err(anyhow::anyhow!("{err:?}")),
}
}
}
}
However this is rather cumbersome, and force to Box the error...
Do you see any better approach for this ?
Footnotes
-
I'm doing this to have a codebase using SQLite on native and IndexedDb on web ↩
Metadata
Metadata
Assignees
Labels
No labels