Skip to main content

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.