Field handlers

It's possible to write an own field handler. Do it, because

  • You want to build an SQL expression with a function.
  • You want to support a database function through FN
  • You want to abuild a filter condition with a function

Filter on fields

Let's support a length function LLE , so that we can filter on maximum word length

#   #[tokio::main(flavor="current_thread")]
#   async fn main() {
   use toql::prelude::{ToqlApi, DefaultFieldHandler, FieldHandler, 
               SqlExpr, SqlBuilderError, FieldFilter, Cache, Toql,
               ParameterMap, sql_expr, query};
   use toql::mock_db::MockDb;

   struct LengthFieldHandler{
       // The default field handler gives us default filters 
       // such as `EQ`, `NE`, ...
       default_handler: DefaultFieldHandler, 
   };

   impl FieldHandler for LengthFieldHandler
   {
       fn build_filter(
           &self,
           select: SqlExpr,            // Column or SQL expression
           filter: &FieldFilter,       // The filter called with this field
           aux_params: &ParameterMap,  // All aux params available
       ) -> Result<Option<SqlExpr>, SqlBuilderError> {
           match filter {
               // Support our custom LLE filter that maps 
               // to the MySQL LENGTH function
               FieldFilter::Fn(name, args) => match name.as_str() {
                   "LLE" => {
                       if args.len() != 1 {
                           return Err(SqlBuilderError::FilterInvalid( 
                               "filter `FN LLE` expects exactly 1 argument".to_string()));
                       }
                       Ok(Some(sql_expr!("LENGTH ({}) <= ?", select, &args[0])))
                   }
                   name @ _ => Err(SqlBuilderError::FilterInvalid(name.to_string())),
               },
               _ => self.default_handler.build_filter(select, filter, aux_params),
           }
       }

   }

   // Getter method for mapper
   pub fn length_field_handler() -> impl FieldHandler {
       LengthFieldHandler{
           default_handler: DefaultFieldHandler::new(), 
       }
   }

   #[derive(Toql)]
   struct User {
       #[toql(key)]
       id: u64,

       #[toql(handler="length_field_handler")]
       name: Option<String>,
   }

   let cache = Cache::new();
   let mut toql = MockDb::from(&cache);

   let q = query!(User, "name FN LLE 5"); 
   let mut _users = toql.load_many(&q).await.unwrap(); 
   assert_eq!(toql.take_unsafe_sql(), 
           "SELECT user.id, user.name \
           FROM User user \
           WHERE LENGTH (user.name) <= 5");
#   }

For a bigger example, check out our permission handler.

Field handlers with local aux params

If you want to use the same field handler in different places it mightly come handy to give the field handler some local context.

This can be achieved with local aux_params:

    #[toql(sql="", 
        field_handler="smart_name_handler", 
        aux_param(name="strategy", value="quick"))]
    smart_name: String

The aux param strategy is only available in the smart_name_handler. Only strings values are supported.