I'm working on a filing-based storage system for an API I'm building. Different kinds of records will be stored in different tables, and since I'm using sqlx's query!
macros, the table name has to be explicitly spelled out in the query body. It can't come from a variable, a function, or any other expression.
To support this, I've defined a trait that handles constructing an appropriate query for a given record type, but I've hit a snag. The return type of query_as!
is Map<'a, Postgres, F, PgArguments>
, where F: FnMut(<Postgres as Database>::Row) -> Result<T> + Send
and T
is just my record type - in this case, mostly serde_json::Value
. This means that the actual return type is not nameable - query_as!
emits a lambda expression which implements FnMut
, and since it's an anonymous type, I can't use it in a trait.
I've been able to work around this by switching to nightly and defining the trait as
pub trait Create {
type Mapper: FnMut(<Postgres as Database>::Row) -> Result<Filed<Value>> + Send;
fn create<'a>(
submission: Submission<Value>,
) -> Map<'a, Postgres, Self::Mapper, PgArguments>;
}
impl Create for crate::bootstrap::records::Bootstrap {
type Mapper = impl FnMut(<Postgres as Database>::Row) -> sqlx::Result<Filed<Value>>;
fn create<'a>(
submission: Submission<Value>,
) -> Map<'a, Postgres, Self::Mapper, PgArguments> {
query_as! {
Filed,
r#"
insert into bootstrap
(number, operation, identity, record, revision)
values
(
$1,
$2,
$3,
$4,
$5
)
returning
number, revision, record
"#,
submission.number, submission.operation, submission.identity, submission.record, submission.revision
}
}
}
However, the type Mapper = impl …
syntax is unstable. Is there some way I can restructure this to avoid using it, and to be able to use stable?
For reference:
-
sqlx::query::Map
: Map in sqlx::query - Rust -
The call site:
pub struct Repository { pool: PgPool, } impl Repository { /* … */ pub async fn current<N, T>(&self, number: N) -> Result<Option<Filed<T>>> where N: Serialize, T: DeserializeOwned + schema::Current, { let number = serde_json::to_string(&number)?; let result = T::current(number).fetch_optional(&self.pool).await?; let result = result.map(Filed::into_app_type).transpose()?; Ok(result) } }