| Crates.io | cf-modkit-sdk |
| lib.rs | cf-modkit-sdk |
| version | 0.1.0 |
| created_at | 2026-01-25 13:59:51.330834+00 |
| updated_at | 2026-01-25 13:59:51.330834+00 |
| description | ModKit SDK |
| homepage | |
| repository | https://github.com/hypernetix/hyperspot |
| max_upload_size | |
| id | 2068786 |
| size | 91,533 |
Security Context Scoping and Typed OData Query Builder for Clients
This crate provides two main features:
SecurityContext to any client typeuse modkit_sdk::WithSecurityContext;
use modkit_security::SecurityContext;
let client = MyClient::new();
let ctx = SecurityContext::root();
// Bind the security context to the client
let secured = client.security_ctx(&ctx);
// Access the client and context
let client_ref = secured.client();
let ctx_ref = secured.ctx();
The typed OData query builder provides a type-safe way to construct OData queries without manually building ODataQuery instances. It ensures compile-time type checking for field references and operations.
use modkit_sdk::odata::{Schema, FieldRef};
// Define field enum
#[derive(Copy, Clone, Eq, PartialEq)]
enum UserField {
Id,
Name,
Email,
Age,
}
// Define schema struct
struct UserSchema;
// Implement Schema trait
impl Schema for UserSchema {
type Field = UserField;
fn field_name(field: Self::Field) -> &'static str {
match field {
UserField::Id => "id",
UserField::Name => "name",
UserField::Email => "email",
UserField::Age => "age",
}
}
}
// Define typed field constants
const ID: FieldRef<UserSchema, uuid::Uuid> = FieldRef::new(UserField::Id);
const NAME: FieldRef<UserSchema, String> = FieldRef::new(UserField::Name);
const EMAIL: FieldRef<UserSchema, String> = FieldRef::new(UserField::Email);
const AGE: FieldRef<UserSchema, i32> = FieldRef::new(UserField::Age);
use modkit_sdk::odata::{QueryBuilder, FilterExpr};
use modkit_odata::SortDir;
// Simple equality filter
let user_id = uuid::Uuid::new_v4();
let query = QueryBuilder::<UserSchema>::new()
.filter(ID.eq(user_id))
.build();
// Complex filter with AND/OR
let query = QueryBuilder::<UserSchema>::new()
.filter(
AGE.ge(18)
.and(AGE.le(65))
.and(NAME.contains("smith"))
)
.order_by(NAME, SortDir::Asc)
.page_size(50)
.build();
// Full query with all features
let query = QueryBuilder::<UserSchema>::new()
.filter(ID.eq(user_id).and(AGE.gt(18)))
.order_by(NAME, SortDir::Asc)
.order_by(AGE, SortDir::Desc)
.select([NAME, EMAIL])
.page_size(25)
.build();
eq(value) - Equality: field eq valuene(value) - Not equal: field ne valuegt(value) - Greater than: field gt valuege(value) - Greater or equal: field ge valuelt(value) - Less than: field lt valuele(value) - Less or equal: field le valuecontains(value) - Contains: contains(field, 'value')startswith(value) - Starts with: startswith(field, 'value')endswith(value) - Ends with: endswith(field, 'value')and(expr) - Logical AND: expr1 and expr2or(expr) - Logical OR: expr1 or expr2not() - Logical NOT: not exprfilter(expr) - Set the filter expressionorder_by(field, dir) - Add an order-by clause (can be called multiple times)select(fields) - Set field projection (pass &[&field1, &field2, ...])page_size(limit) - Set the page size limitbuild() - Build the final ODataQuery with computed filter hashThe query builder enforces type safety at compile time:
// ✅ Correct: String field with string operations
let query = QueryBuilder::<UserSchema>::new()
.filter(NAME.contains("john"))
.build();
// ❌ Compile error: contains() only available for String fields
let query = QueryBuilder::<UserSchema>::new()
.filter(AGE.contains("test")) // Won't compile!
.build();
// ✅ Correct: Comparison operations work on all types
let query = QueryBuilder::<UserSchema>::new()
.filter(AGE.gt(18))
.build();
The query builder automatically computes a stable, deterministic hash for filter expressions using the same algorithm as modkit_odata::pagination::short_filter_hash. This ensures cursor pagination consistency:
let user_id = uuid::Uuid::new_v4();
let query1 = QueryBuilder::<UserSchema>::new()
.filter(ID.eq(user_id))
.build();
let query2 = QueryBuilder::<UserSchema>::new()
.filter(ID.eq(user_id))
.build();
// Same filter produces same hash
assert_eq!(query1.filter_hash, query2.filter_hash);
The following Rust types can be used in filter expressions:
booluuid::UuidString and &stri32, i64, u32, u64Additional types can be supported by implementing the IntoODataValue trait.
See examples/typed_odata_query.rs for comprehensive examples demonstrating:
Run the example:
cargo run --package modkit-sdk --example typed_odata_query
modkit_odata::ast::Expr for maximum flexibilityThe query builder includes comprehensive unit tests verifying:
Run tests:
cargo test --package modkit-sdk --lib odata
See workspace license.