API Reference
This page covers using Open PQL as a Rust library, including core types, functions, and integration patterns.
Installation
Add Open PQL to your Cargo.toml:
[dependencies]
open-pql = "0.0.3"
Basic Usage
use open_pql::*;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Parse a PQL query
let query = "select equity from hero='AhKh', villain='QQ+', game='holdem'";
// Execute the query (implementation depends on your needs)
// This is a simplified example
println!("Query: {}", query);
Ok(())
}
Core Types
Cards and Hands
use open_pql::{Card, Hand, Rank, Suit};
// Create individual cards
let ace_of_hearts = Card::new(Rank::Ace, Suit::Hearts);
let king_of_spades = Card::new(Rank::King, Suit::Spades);
// Create a hand
let hand = Hand::new([ace_of_hearts, king_of_spades]);
// Parse from string
let card: Card = "Ah".parse()?;
let hand: Hand = "AhKs".parse()?;
Board Representation
use open_pql::{Board, Flop, Turn, River};
// Create boards for different streets
let flop: Flop = "AhKs2c".parse()?;
let turn: Turn = "AhKs2c4d".parse()?;
let river: River = "AhKs2c4d7h".parse()?;
// Generic board
let board: Board = flop.into();
Ranges
use open_pql::PQLRange;
// Parse hand ranges
let range: PQLRange = "22+,A2+,K5+".parse()?;
let premium_range: PQLRange = "JJ+,AK".parse()?;
// Check if a hand is in range
let hand: Hand = "AhKs".parse()?;
let is_in_range = range.contains(&hand);
Game Types
use open_pql::PQLGame;
let holdem = PQLGame::Holdem;
let shortdeck = PQLGame::ShortDeck;
// More variants coming soon
PQL Parser Integration
Query Parsing
use open_pql::{parse_pql, PQLQuery, PQLError};
fn parse_and_analyze(query_str: &str) -> Result<(), PQLError> {
// Parse the PQL query
let query = parse_pql(query_str)?;
// Analyze the parsed query
match query {
PQLQuery::Select { expressions, context, conditions } => {
println!("SELECT query with {} expressions", expressions.len());
// Process the query components
}
// Handle other query types
}
Ok(())
}
Expression Evaluation
use open_pql::{evaluate_expression, PQLExpression, PQLContext};
fn evaluate_equity(context: PQLContext) -> Result<f64, PQLError> {
let expr = PQLExpression::Equity;
let result = evaluate_expression(&expr, &context)?;
match result {
PQLValue::Double(equity) => Ok(equity),
_ => Err(PQLError::TypeMismatch),
}
}
Hand Evaluation
Basic Hand Strength
use open_pql::{evaluate_hand, HandType, PQLGame};
fn analyze_hand(hand: Hand, board: Board, game: PQLGame) -> HandType {
let strength = evaluate_hand(&hand, &board, game);
strength.hand_type()
}
// Example usage
let hand: Hand = "AhKh".parse().unwrap();
let board: Board = "AcKs2d".parse().unwrap();
let hand_type = analyze_hand(hand, board, PQLGame::Holdem);
assert_eq!(hand_type, HandType::TwoPair);
Hand Rankings
use open_pql::{hand_ranking, PQLHiRating};
fn get_hand_strength(hand: Hand, board: Board, game: PQLGame) -> PQLHiRating {
hand_ranking(&hand, &board, game)
}
Equity Calculations
Two-Player Equity
use open_pql::{calculate_equity, EquityResult};
fn heads_up_equity(
hero: Hand,
villain: Hand,
board: Board,
game: PQLGame
) -> EquityResult {
calculate_equity(&[hero, villain], &board, game)
}
// Example
let hero: Hand = "AhKh".parse().unwrap();
let villain: Hand = "QcQd".parse().unwrap();
let board: Board = "".parse().unwrap(); // Preflop
let result = heads_up_equity(hero, villain, board, PQLGame::Holdem);
println!("Hero equity: {:.1}%", result.equity[0] * 100.0);
Range vs Range
use open_pql::{calculate_range_equity, PQLRange};
fn range_equity(
hero_range: PQLRange,
villain_range: PQLRange,
board: Board,
game: PQLGame
) -> f64 {
calculate_range_equity(&hero_range, &villain_range, &board, game)
}
Advanced Features
Monte Carlo Simulations
use open_pql::{MonteCarloSimulation, SimulationConfig};
fn run_simulation(
players: &[Hand],
board: Board,
game: PQLGame,
iterations: u32
) -> Result<Vec<f64>, PQLError> {
let config = SimulationConfig {
iterations,
random_seed: None,
..Default::default()
};
let mut simulation = MonteCarloSimulation::new(players, board, game, config);
simulation.run()
}
Custom Functions
use open_pql::{PQLFunction, PQLValue, PQLContext};
struct CustomFunction;
impl PQLFunction for CustomFunction {
fn name(&self) -> &str {
"custom_analysis"
}
fn evaluate(&self, args: &[PQLValue], context: &PQLContext) -> Result<PQLValue, PQLError> {
// Implement your custom logic here
Ok(PQLValue::Boolean(true))
}
}
// Register the custom function
fn register_custom_functions() {
open_pql::register_function(Box::new(CustomFunction));
}
Error Handling
Common Error Types
use open_pql::PQLError;
fn handle_pql_error(result: Result<PQLValue, PQLError>) {
match result {
Ok(value) => println!("Result: {:?}", value),
Err(PQLError::ParseError(msg)) => eprintln!("Parse error: {}", msg),
Err(PQLError::InvalidHand(hand)) => eprintln!("Invalid hand: {}", hand),
Err(PQLError::InvalidRange(range)) => eprintln!("Invalid range: {}", range),
Err(PQLError::TypeMismatch) => eprintln!("Type mismatch in expression"),
Err(PQLError::RuntimeError(msg)) => eprintln!("Runtime error: {}", msg),
}
}
Result Handling Patterns
use open_pql::{PQLResult, PQLValue};
// Using ? operator
fn calculate_with_error_propagation() -> PQLResult<f64> {
let hand: Hand = "AhKh".parse()?;
let board: Board = "AcKs2d".parse()?;
let equity = calculate_equity(&[hand], &board, PQLGame::Holdem)?;
Ok(equity.equity[0])
}
// Pattern matching
fn calculate_with_pattern_matching() -> Option<f64> {
match "AhKh".parse::<Hand>() {
Ok(hand) => {
// Continue with calculation
Some(0.5) // Placeholder
}
Err(_) => None,
}
}
Integration Patterns
Building a Custom Analyzer
use open_pql::*;
pub struct PokerAnalyzer {
game: PQLGame,
}
impl PokerAnalyzer {
pub fn new(game: PQLGame) -> Self {
Self { game }
}
pub fn analyze_spot(&self, hero: &str, villain: &str, board: &str) -> Result<AnalysisResult, PQLError> {
let hero_hand: Hand = hero.parse()?;
let villain_hand: Hand = villain.parse()?;
let board: Board = board.parse()?;
let equity = calculate_equity(&[hero_hand, villain_hand], &board, self.game)?;
let hero_strength = evaluate_hand(&hero_hand, &board, self.game);
Ok(AnalysisResult {
equity: equity.equity[0],
hand_strength: hero_strength,
// Add more analysis...
})
}
}
#[derive(Debug)]
pub struct AnalysisResult {
pub equity: f64,
pub hand_strength: PQLHiRating,
// Add more fields as needed
}
Async Integration
use open_pql::*;
use tokio;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let analyzer = PokerAnalyzer::new(PQLGame::Holdem);
// Run analysis in async context
let result = tokio::task::spawn_blocking(move || {
analyzer.analyze_spot("AhKh", "QcQd", "AcKs2d")
}).await??;
println!("Analysis result: {:?}", result);
Ok(())
}
Web Service Integration
use open_pql::*;
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
struct AnalysisRequest {
hero: String,
villain: String,
board: String,
game: String,
}
#[derive(Serialize)]
struct AnalysisResponse {
equity: f64,
hand_type: String,
error: Option<String>,
}
fn process_request(req: AnalysisRequest) -> AnalysisResponse {
let game = match req.game.as_str() {
"holdem" => PQLGame::Holdem,
"shortdeck" => PQLGame::ShortDeck,
_ => return AnalysisResponse {
equity: 0.0,
hand_type: "".to_string(),
error: Some("Invalid game type".to_string()),
},
};
match analyze_hands(&req.hero, &req.villain, &req.board, game) {
Ok((equity, hand_type)) => AnalysisResponse {
equity,
hand_type: format!("{:?}", hand_type),
error: None,
},
Err(e) => AnalysisResponse {
equity: 0.0,
hand_type: "".to_string(),
error: Some(format!("{:?}", e)),
},
}
}
fn analyze_hands(hero: &str, villain: &str, board: &str, game: PQLGame) -> PQLResult<(f64, HandType)> {
let hero_hand: Hand = hero.parse()?;
let villain_hand: Hand = villain.parse()?;
let board: Board = board.parse()?;
let equity = calculate_equity(&[hero_hand, villain_hand], &board, game)?;
let hand_strength = evaluate_hand(&hero_hand, &board, game);
Ok((equity.equity[0], hand_strength.hand_type()))
}
Performance Considerations
Optimal Usage Patterns
// Good: Reuse parsed objects
let hero: Hand = "AhKh".parse()?;
let villain: Hand = "QcQd".parse()?;
let board: Board = "AcKs2d".parse()?;
for _ in 0..1000 {
let result = calculate_equity(&[hero, villain], &board, PQLGame::Holdem)?;
}
// Avoid: Reparsing in loops
for _ in 0..1000 {
let hero: Hand = "AhKh".parse()?; // Inefficient
let result = calculate_equity(&[hero], &Board::default(), PQLGame::Holdem)?;
}
Memory Management
use open_pql::*;
// Use stack allocation when possible
fn efficient_analysis() -> PQLResult<f64> {
const HERO: &str = "AhKh";
const VILLAIN: &str = "QcQd";
let hero: Hand = HERO.parse()?;
let villain: Hand = VILLAIN.parse()?;
// Stack-allocated arrays are efficient for small numbers of players
let players = [hero, villain];
let result = calculate_equity(&players, &Board::default(), PQLGame::Holdem)?;
Ok(result.equity[0])
}
For complete API documentation with all available functions and types, visit docs.rs/open-pql.