我试图为我的diesel应用程序提供一个通用的过滤器功能。用户将指定一个要过滤的列,该列的适当值(str,float,int等),然后执行以下操作之一:
pub enum FilterOp {
NONE,
EQUAL,
LESS_THAN,
LESS_THAN_EQUAL,
GREATER_THAN,
GREATER_THAN_EQUAL,
CONTAINS,
NOT_EQUAL,
STRING_LIKE,
}
字符串
如果我的table有:
diesel::table! {
use diesel::sql_types::*;
use postgis_diesel::sql_types::*;
entities (uid) {
uid -> Uuid,
source -> Text,
source_id -> Text,
...
型
所以对于“source_id”列,我最终得到这样的代码:
let mut query: schema::entities::BoxedQuery<diesel::pg::Pg> = schema::entities::table.into_boxed();
if "source_id".eq( fitler_column.as_str() ) {
...
match op {
FilterOp::EQUAL => {
query = query.filter( source_id.eq( filter_value.string_value() ) );
},
FilterOp::NOT_EQUAL => {
query = query.filter( source_id.ne( filter_value.string_value() ) );
},
FilterOp::LESS_THAN => {
query = query.filter( source_id.lt( filter_value.string_value() ) );
},
型
对于其他列类型,这也是类似的(一个uuid字符串将被转换为一个Uuid示例),但这是一个大量重复的代码(对于每一列)。
所以我想提取出匹配,类似于:(对于“源”列):
query_filter(op,
&mut query,
&schema::entities::dsl::source,
filter_value.string_value() );
型
但我正在努力正确定义query_filter。它需要看起来像这样:
fn query_filter<T, V> ( op: FilterOp,
query: &mut schema::entities::BoxedQuery<diesel::pg::Pg>,
column: &T,
filter_value: &V )
{
use crate::schema::entities::dsl::*;
match op {
FilterOp::EQUAL => {
*query = query.filter( column.eq( filter_value ) );
},
...
型
因此,这将参数化列,它是SQL类型(T)和相应的原生Rust类型(V)。我对T和V类型应该声明为什么感到困惑。
这需要宏吗?
谢谢你,谢谢
1条答案
按热度按时间rjzwgtxy1#
由于你的帖子并没有真正包含一个最小的例子,我已经建立了以下一个:
字符串
现在,在我们进入“解决方案”之前,首先警告一句:为diesel编写抽象代码是可能的,但需要处理大量的trait边界。因此,在开始玩弄这类代码之前,请确保您对rusts trait系统的工作原理有很好的理解。
现在需要回答的第一件事是如何限制类型
C
和V
。我们可以在这里查看diesel文档,发现有一个名为Column
的trait,这似乎适合我们对C
的使用情况。否则,我们也可以看看ExpressionMethods::eq
方法,并看到它对所有实现Expression
的类型都实现了。对于V
,我们再次查看eq
方法,发现参数绑定在T: AsExpression<Self::SqlType>,
上。这表明我们需要在同一个trait上限制V
。这就给我们留下了以下起始trait边界:
C: Column
和V: AsExpression<C::SqlType>
。如果我们尝试编译器抱怨“trait
diesel::sql_types::SqlType
没有为<C as diesel::Expression>::SqlType
实现“,那么我们添加建议的trait绑定:C::SqlType: SqlType
如果我们尝试编译器再次抱怨“方法
eq
存在于类型参数C
中,但它的trait bounds不被满足”。现在它给出了毫无意义的建议,将C
限制为Iterator
。帮助还包含以下代码块:型
因此,让我们尝试
C: SingleValue
建议,而不是因为这是一些柴油特质。如果我们尝试,编译器再次抱怨“trait bound
C: ValidGrouping<()>
is not satisfied”,并建议添加C: ValidGrouping<()>
。如果我们尝试编译器再次抱怨“trait bound
<V as AsExpression<<C as diesel::Expression>::SqlType>>::Expression: ValidGrouping<()>
is not satisfied”,并建议添加该bound。如果我们尝试编译器再次抱怨“trait bound
<C as ValidGrouping<()>>::IsAggregate: MixedAggregates<<<V as AsExpression<<C as diesel::Expression>::SqlType>>::Expression as ValidGrouping<()>>::IsAggregate>
is not satisfied”并建议添加另一个trait bound,建议的bound可以通过使用<C as ValidGrouping<()>>::IsAggregate:MixedAggregates<<dsl::AsExpr<V, C> as ValidGrouping<()>>::IsAggregate>
更容易地编写。如果我们再次尝试,编译器会再次抱怨“trait bound
<<C as ValidGrouping<()>>::IsAggregate as MixedAggregates<<<V as AsExpression<<C as diesel::Expression>::SqlType>>::Expression as ValidGrouping<()>>::IsAggregate>>::Output: MixedAggregates<diesel::expression::is_aggregate::No>
is not satisfied”并建议另一个trait bound。不幸的是,这个建议是一个陷阱。相反,我们需要将最后一个绑定修改为<C as ValidGrouping<()>>::IsAggregate:MixedAggregates<<dsl::AsExpr<V, C> as ValidGrouping<()>>::IsAggregate, Output = is_aggregate::No>
。如果我们这样做,编译器会再次抱怨“trait bound
<<C as diesel::Expression>::SqlType as diesel::sql_types::SqlType>::IsNull: OneIsNullable<<<C as diesel::Expression>::SqlType as diesel::sql_types::SqlType>::IsNull>
is not satisfied”,并建议另一个长trait bound。这可以通过使用“required forexpression::operators::Eq<C, <V as AsExpression<<C as diesel::Expression>::SqlType>>::Expression>
to implementdiesel::Expression
“行中的bound来简化。这给了我们以下的bound:dsl::Eq<C, V>: Expression
如果我们这样做,编译器会再次抱怨“trait bound
<expression::grouped::Grouped<expression::operators::Eq<C, <V as AsExpression<<C as diesel::Expression>::SqlType>>::Expression>> as diesel::Expression>::SqlType: BoolOrNullableBool
is not satisfied”并建议另一个trait bound。如果我们尝试编译器再次抱怨“trait bound
C: AppearsOnTable<users::table>
is not satisfied”并建议添加另一个trait bound。该bound有点帮助,但最好基于以下注解行中的trait bound之一添加一个bound:“required forexpression::operators::Eq<C, <V as AsExpression<<C as diesel::Expression>::SqlType>>::Expression>
to implementAppearsOnTable<users::table>
“。这将导致bounddsl::Eq<C, V>: AppearsOnTable<users::table>
如果我们尝试编译器再次抱怨“trait bound
C: QueryFragment<Sqlite>
is not satisfied”。同样,最好根据所需的because. lines将trait绑定添加到dsl::Eq
。如果我们尝试编译器再次抱怨“
C
不能在线程之间安全地发送”,那么最好再次将trait绑定到dsl::Eq
。如果我们尝试编译器抱怨“关联的类型
<V as AsExpression<<C as diesel::Expression>::SqlType>>::Expression
可能活得不够长”,并表示BoxedQuery
类型中存在隐藏的生存期边界。因此,我们在此添加命名生存期'a
,并限制相关类型的生存期至少与'a
一样长。C
也会发出相同的错误消息,因此我们添加边界那里也是。添加这些边界后,代码最终编译。这给出了以下代码:
型
前5个边界是所有运算符都需要的通用边界。最后2个边界(两行都包含
dsl::Eq
)是您在此函数中使用的运算符特定的边界。如果您使用多个运算符,则需要复制这些行并针对其他运算符进行调整。例如,为了支持.ne()
,需要以下附加边界:型
总的信息是: