sal/postgresclient/src/rhai.rs
Mahmoud-Emad b737cd6337
Some checks are pending
Rhai Tests / Run Rhai Tests (push) Waiting to run
feat: convert postgresclient module to independent sal-postgresclient package
- Move src/postgresclient/ to postgresclient/ package structure
- Add comprehensive test suite (28 tests) with real PostgreSQL operations
- Maintain Rhai integration with all 10 wrapper functions
- Update workspace configuration and dependencies
- Add complete documentation with usage examples
- Remove old module and update all references
- Ensure zero regressions in existing functionality

Closes: postgresclient monorepo conversion
2025-06-23 03:12:26 +03:00

361 lines
11 KiB
Rust

//! Rhai wrappers for PostgreSQL client module functions
//!
//! This module provides Rhai wrappers for the functions in the PostgreSQL client module.
use crate::{
create_database, execute, execute_sql, get_postgres_client, install_postgres,
is_postgres_running, query_one, reset, PostgresInstallerConfig,
};
use postgres::types::ToSql;
use rhai::{Array, Engine, EvalAltResult, Map};
use sal_virt::nerdctl::Container;
/// Register PostgreSQL client module functions with the Rhai engine
///
/// # Arguments
///
/// * `engine` - The Rhai engine to register the functions with
///
/// # Returns
///
/// * `Result<(), Box<EvalAltResult>>` - Ok if registration was successful, Err otherwise
pub fn register_postgresclient_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
// Register PostgreSQL connection functions
engine.register_fn("pg_connect", pg_connect);
engine.register_fn("pg_ping", pg_ping);
engine.register_fn("pg_reset", pg_reset);
// Register basic query functions
engine.register_fn("pg_execute", pg_execute);
engine.register_fn("pg_query", pg_query);
engine.register_fn("pg_query_one", pg_query_one);
// Register installer functions
engine.register_fn("pg_install", pg_install);
engine.register_fn("pg_create_database", pg_create_database);
engine.register_fn("pg_execute_sql", pg_execute_sql);
engine.register_fn("pg_is_running", pg_is_running);
// Builder pattern functions will be implemented in a future update
Ok(())
}
/// Connect to PostgreSQL using environment variables
///
/// # Returns
///
/// * `Result<bool, Box<EvalAltResult>>` - true if successful, error otherwise
pub fn pg_connect() -> Result<bool, Box<EvalAltResult>> {
match get_postgres_client() {
Ok(_) => Ok(true),
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("PostgreSQL error: {}", e).into(),
rhai::Position::NONE,
))),
}
}
/// Ping the PostgreSQL server
///
/// # Returns
///
/// * `Result<bool, Box<EvalAltResult>>` - true if successful, error otherwise
pub fn pg_ping() -> Result<bool, Box<EvalAltResult>> {
match get_postgres_client() {
Ok(client) => match client.ping() {
Ok(result) => Ok(result),
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("PostgreSQL error: {}", e).into(),
rhai::Position::NONE,
))),
},
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("PostgreSQL error: {}", e).into(),
rhai::Position::NONE,
))),
}
}
/// Reset the PostgreSQL client connection
///
/// # Returns
///
/// * `Result<bool, Box<EvalAltResult>>` - true if successful, error otherwise
pub fn pg_reset() -> Result<bool, Box<EvalAltResult>> {
match reset() {
Ok(_) => Ok(true),
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("PostgreSQL error: {}", e).into(),
rhai::Position::NONE,
))),
}
}
/// Execute a query on the PostgreSQL connection
///
/// # Arguments
///
/// * `query` - The query to execute
///
/// # Returns
///
/// * `Result<i64, Box<EvalAltResult>>` - The number of rows affected if successful, error otherwise
pub fn pg_execute(query: &str) -> Result<i64, Box<EvalAltResult>> {
// We can't directly pass dynamic parameters from Rhai to PostgreSQL
// So we'll only support parameterless queries for now
let params: &[&(dyn ToSql + Sync)] = &[];
match execute(query, params) {
Ok(rows) => Ok(rows as i64),
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("PostgreSQL error: {}", e).into(),
rhai::Position::NONE,
))),
}
}
/// Execute a query on the PostgreSQL connection and return the rows
///
/// # Arguments
///
/// * `query` - The query to execute
///
/// # Returns
///
/// * `Result<Array, Box<EvalAltResult>>` - The rows if successful, error otherwise
pub fn pg_query(query_str: &str) -> Result<Array, Box<EvalAltResult>> {
// We can't directly pass dynamic parameters from Rhai to PostgreSQL
// So we'll only support parameterless queries for now
let params: &[&(dyn ToSql + Sync)] = &[];
match crate::query(query_str, params) {
Ok(rows) => {
let mut result = Array::new();
for row in rows {
let mut map = Map::new();
for column in row.columns() {
let name = column.name();
// We'll convert all values to strings for simplicity
let value: Option<String> = row.get(name);
if let Some(val) = value {
map.insert(name.into(), val.into());
} else {
map.insert(name.into(), rhai::Dynamic::UNIT);
}
}
result.push(map.into());
}
Ok(result)
}
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("PostgreSQL error: {}", e).into(),
rhai::Position::NONE,
))),
}
}
/// Execute a query on the PostgreSQL connection and return a single row
///
/// # Arguments
///
/// * `query` - The query to execute
///
/// # Returns
///
/// * `Result<Map, Box<EvalAltResult>>` - The row if successful, error otherwise
pub fn pg_query_one(query: &str) -> Result<Map, Box<EvalAltResult>> {
// We can't directly pass dynamic parameters from Rhai to PostgreSQL
// So we'll only support parameterless queries for now
let params: &[&(dyn ToSql + Sync)] = &[];
match query_one(query, params) {
Ok(row) => {
let mut map = Map::new();
for column in row.columns() {
let name = column.name();
// We'll convert all values to strings for simplicity
let value: Option<String> = row.get(name);
if let Some(val) = value {
map.insert(name.into(), val.into());
} else {
map.insert(name.into(), rhai::Dynamic::UNIT);
}
}
Ok(map)
}
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("PostgreSQL error: {}", e).into(),
rhai::Position::NONE,
))),
}
}
/// Install PostgreSQL using nerdctl
///
/// # Arguments
///
/// * `container_name` - Name for the PostgreSQL container
/// * `version` - PostgreSQL version to install (e.g., "latest", "15", "14")
/// * `port` - Port to expose PostgreSQL on
/// * `username` - Username for PostgreSQL
/// * `password` - Password for PostgreSQL
///
/// # Returns
///
/// * `Result<bool, Box<EvalAltResult>>` - true if successful, error otherwise
pub fn pg_install(
container_name: &str,
version: &str,
port: i64,
username: &str,
password: &str,
) -> Result<bool, Box<EvalAltResult>> {
// Create the installer configuration
let config = PostgresInstallerConfig::new()
.container_name(container_name)
.version(version)
.port(port as u16)
.username(username)
.password(password);
// Install PostgreSQL
match install_postgres(config) {
Ok(_) => Ok(true),
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("PostgreSQL installer error: {}", e).into(),
rhai::Position::NONE,
))),
}
}
/// Create a new database in PostgreSQL
///
/// # Arguments
///
/// * `container_name` - Name of the PostgreSQL container
/// * `db_name` - Database name to create
///
/// # Returns
///
/// * `Result<bool, Box<EvalAltResult>>` - true if successful, error otherwise
pub fn pg_create_database(container_name: &str, db_name: &str) -> Result<bool, Box<EvalAltResult>> {
// Create a container reference
let container = Container {
name: container_name.to_string(),
container_id: Some(container_name.to_string()), // Use name as ID for simplicity
image: None,
config: std::collections::HashMap::new(),
ports: Vec::new(),
volumes: Vec::new(),
env_vars: std::collections::HashMap::new(),
network: None,
network_aliases: Vec::new(),
cpu_limit: None,
memory_limit: None,
memory_swap_limit: None,
cpu_shares: None,
restart_policy: None,
health_check: None,
detach: false,
snapshotter: None,
};
// Create the database
match create_database(&container, db_name) {
Ok(_) => Ok(true),
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("PostgreSQL error: {}", e).into(),
rhai::Position::NONE,
))),
}
}
/// Execute a SQL script in PostgreSQL
///
/// # Arguments
///
/// * `container_name` - Name of the PostgreSQL container
/// * `db_name` - Database name
/// * `sql` - SQL script to execute
///
/// # Returns
///
/// * `Result<String, Box<EvalAltResult>>` - Output of the command if successful, error otherwise
pub fn pg_execute_sql(
container_name: &str,
db_name: &str,
sql: &str,
) -> Result<String, Box<EvalAltResult>> {
// Create a container reference
let container = Container {
name: container_name.to_string(),
container_id: Some(container_name.to_string()), // Use name as ID for simplicity
image: None,
config: std::collections::HashMap::new(),
ports: Vec::new(),
volumes: Vec::new(),
env_vars: std::collections::HashMap::new(),
network: None,
network_aliases: Vec::new(),
cpu_limit: None,
memory_limit: None,
memory_swap_limit: None,
cpu_shares: None,
restart_policy: None,
health_check: None,
detach: false,
snapshotter: None,
};
// Execute the SQL script
match execute_sql(&container, db_name, sql) {
Ok(output) => Ok(output),
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("PostgreSQL error: {}", e).into(),
rhai::Position::NONE,
))),
}
}
/// Check if PostgreSQL is running
///
/// # Arguments
///
/// * `container_name` - Name of the PostgreSQL container
///
/// # Returns
///
/// * `Result<bool, Box<EvalAltResult>>` - true if running, false otherwise, or error
pub fn pg_is_running(container_name: &str) -> Result<bool, Box<EvalAltResult>> {
// Create a container reference
let container = Container {
name: container_name.to_string(),
container_id: Some(container_name.to_string()), // Use name as ID for simplicity
image: None,
config: std::collections::HashMap::new(),
ports: Vec::new(),
volumes: Vec::new(),
env_vars: std::collections::HashMap::new(),
network: None,
network_aliases: Vec::new(),
cpu_limit: None,
memory_limit: None,
memory_swap_limit: None,
cpu_shares: None,
restart_policy: None,
health_check: None,
detach: false,
snapshotter: None,
};
// Check if PostgreSQL is running
match is_postgres_running(&container) {
Ok(running) => Ok(running),
Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
format!("PostgreSQL error: {}", e).into(),
rhai::Position::NONE,
))),
}
}