aip-160 0.1.0

A Rust implementation of Google AIP-160 filtering standard.
Documentation

AIP Filter Parser with SeaORM Support

A Rust implementation of Google AIP-160 filtering standard with SeaORM integration.

Features

  • πŸ” Full AIP-160 Support - Parse Google AIP-160 filter expressions
  • πŸ—„οΈ SeaORM Integration - Convert filters directly to SeaORM conditions (3 easy ways!)
  • 🎯 Type Safe - Strongly typed AST and error handling
  • ⚑ Fast - Built with the Pest parser for performance
  • πŸ§ͺ Well Tested - Comprehensive test coverage
  • πŸͺ„ Zero Boilerplate - One-line macro for instant SeaORM integration

Installation

[dependencies]
# Basic parsing only
aip-160 = "0.1"

# With SeaORM support
aip-160 = { version = "0.1", features = ["sea-orm"] }

Quick Start

Basic Parsing

use aip_160::parse_filter;

fn main() {
    let filter = parse_filter("name = \"John\" AND age > 18").unwrap();
    println!("{}", filter);
}

SeaORM Integration

There are 3 ways to integrate with SeaORM, from simplest to most flexible:

Method 1: Using the Macro (Recommended - Simplest)

use aip_160::{impl_field_mapper, parse_filter, ToSeaOrmCondition};
use sea_orm::entity::prelude::*;

// Define your entity as usual
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "users")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i32,
    pub name: String,
    pub email: String,
    pub age: i32,
}

// That's it! One line to enable filtering
impl_field_mapper!(Entity, Column);

// Use it
async fn find_users(db: &DatabaseConnection, filter_str: &str) -> Result<Vec<Model>, DbErr> {
    let filter = parse_filter(filter_str)?;
    let condition = filter.to_condition(&Entity)?;
    Entity::find().filter(condition).all(db).await
}

Method 2: Using column_from_str Helper

use aip_160::{column_from_str, parse_filter, FieldMapper, ToSeaOrmCondition};
use sea_orm::entity::prelude::*;

impl FieldMapper for Entity {
    fn map_field(&self, field: &str) -> aip_160::Result<SimpleExpr> {
        // Automatic snake_case β†’ PascalCase conversion
        column_from_str::<Column>(field)
    }
}

Method 3: Manual Mapping (Most Flexible)

use aip_160::{parse_filter, FieldMapper, ToSeaOrmCondition};
use sea_orm::entity::prelude::*;

impl FieldMapper for Entity {
    fn map_field(&self, field: &str) -> aip_160::Result<SimpleExpr> {
        match field {
            "id" => Ok(Column::Id.into_simple_expr()),
            "name" => Ok(Column::Name.into_simple_expr()),
            // Custom mapping: filter field "user_email" β†’ Column::Email
            "user_email" => Ok(Column::Email.into_simple_expr()),
            _ => Err(aip_160::FilterError::InvalidField(field.to_string())),
        }
    }
}

Supported Syntax

Comparison Operators

Operator Description Example
= Equal name = "John"
!= Not equal status != "inactive"
> Greater than age > 18
>= Greater than or equal age >= 18
< Less than age < 65
<= Less than or equal age <= 65
: Contains/Has email : "@gmail.com"

Logical Operators

Operator Description Example
AND Logical AND active = true AND age > 18
OR Logical OR status = "active" OR status = "pending"
NOT Logical NOT NOT deleted = true
( ) Grouping (a = 1 OR a = 2) AND b = 3

Value Types

Type Example Notes
String "value" or 'value' Double or single quotes
Number 42, 3.14, -10, 1e5 Integer or floating point
Boolean true, false Case insensitive
Null null Case insensitive

Example Filters

# Simple equality
name = "John Doe"

# Numeric comparison
age > 18 AND age < 65

# Multiple conditions
active = true AND age > 18 AND verified = true

# OR conditions
status = "active" OR status = "pending"

# Grouped expressions
(status = "active" OR status = "pending") AND age > 18

# Contains/substring matching
email : "@example.com"

# NOT operator
NOT deleted = true

# Complex query
(active = true OR status = "trial") AND age >= 18 AND email : "@company.com"

# Null checks
middle_name = null

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Filter Text β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Parser    β”‚  (Pest PEG Parser)
β”‚ filter.pest β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚    AST      β”‚  (Abstract Syntax Tree)
β”‚   ast.rs    β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ SeaORM Converterβ”‚  (Feature: sea-orm)
β”‚   sea_orm.rs    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ SeaORM Conditionβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

API Reference

Main Functions

parse_filter(input: &str) -> Result<Filter>

Parse a filter string into an AST.

Filter::to_condition<M: FieldMapper>(&self, mapper: &M) -> Result<Condition>

Convert a filter to a SeaORM condition.

Traits

FieldMapper

Implement this trait to map filter field names to SeaORM columns.

pub trait FieldMapper {
    fn map_field(&self, field: &str) -> Result<SimpleExpr>;
}

Types

  • Filter - Root filter structure
  • Expression - Filter expression (AND, OR, NOT, Restriction)
  • Restriction - A comparison (field, comparator, value)
  • Comparator - Comparison operator
  • Value - Filter value (String, Number, Boolean, Null)

Error Handling

The library uses the FilterError enum for all errors:

pub enum FilterError {
    ParseError(String),
    ConversionError(String),
    InvalidField(String),
    UnsupportedOperation(String),
}

Testing

Run the tests:

cargo test

Run the demo:

cargo run

Run the SeaORM example:

cargo run --example sea_orm_example

Features

  • default - Includes sea-orm
  • sea-orm - Enable SeaORM integration

To use without SeaORM:

[dependencies]
aip-filter = { path = "path/to/aip-filter", default-features = false }

Google AIP-160 Compliance

This implementation follows the Google AIP-160 standard for filtering. Key features:

  • βœ… Comparison operators (=, !=, <, <=, >, >=)
  • βœ… Logical operators (AND, OR, NOT)
  • βœ… Grouping with parentheses
  • βœ… String, number, boolean, and null values
  • βœ… Has/contains operator (:)
  • ⚠️ Partial: Function calls (not yet implemented)
  • ⚠️ Partial: Nested field access (parsed but not fully supported in SeaORM conversion)

License

MIT or Apache-2.0

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

References