mapepire 0.4.0

Async Rust client for Mapepire — Db2 for IBM i over secure WebSockets
Documentation

mapepire

CI Audit (daily) deps.rs MSRV License: MIT OR Apache-2.0

Async Rust client SDK for Mapepire — a cloud-friendly access layer for Db2 for IBM i that exposes the database over TLS-secured WebSockets.

Status: v0.4 ready (observability + cleanup). Not yet on crates.io. The full v1.0 surface (real-IBM-i CI, examples) lands in v1.0.

Sibling SDKs exist for Node.js, Python, Java, Go, PHP, and C#/.NET. This crate fills the Rust gap with a parity-first design.

Quick look

use mapepire::{DaemonServer, Pool, TlsConfig};

# async fn example() -> mapepire::Result<()> {
let server = DaemonServer::builder()
    .host("ibmi.example.com")
    .user("DCURTIS")
    .password(std::env::var("MAPEPIRE_PASSWORD").unwrap())
    .tls(TlsConfig::Verified)
    .build()
    .expect("missing required field");

let pool = Pool::builder(server).max_size(8).build().await?;

// One-shot SQL via the routed pool.
let _rows = pool.execute("SELECT 1 FROM SYSIBM.SYSDUMMY1").await?;

// Transactional work via Reserved (BEGIN / DML / COMMIT all on one socket).
let conn = pool.acquire().await?.rollback_on_drop();
conn.execute("BEGIN").await?;
conn.execute_with(
    "UPDATE ORDERS SET STATUS = ? WHERE ID = ?",
    &[serde_json::json!("paid"), serde_json::json!(42)],
).await?;
conn.execute("COMMIT").await?;
# Ok(()) }

Pool::builder(server).max_size(8).build().await? warms a deadpool-backed connection pool of Job handles. pool.execute(...) runs one-shot SQL via the v0.3 §7.3 three-tier routing scan (idle → least-busy → fair-queue). pool.acquire() returns a [Reserved] handle that pins one socket for BEGIN / DML / COMMIT so the entire transaction lands on the same connection. The opt-in .rollback_on_drop() fires a best-effort ROLLBACK if the handle drops without an explicit COMMIT / ROLLBACK.

The password setter takes ownership of a String and immediately moves it into a zeroizing buffer (Password). DaemonServer is not Clone — the Pool::builder constructor takes impl Into<Arc<DaemonServer>> so the single config is shared across every pooled connection.

Runnable examples

The examples/ directory ships six runnable demos covering the common patterns:

Example Demonstrates
examples/one_shot.rs Pool::builderpool.execute(sql) → iterate rows
examples/prepared.rs Job::prepare + Query::execute_with reused across calls
examples/transaction.rs pool.acquire().rollback_on_drop() + v0.4 typed begin/commit
examples/streaming.rs Rows::stream_typed::<T> with a serde::Deserialize row struct
examples/with_tracing.rs tracing-subscriber registration + per-execute span output
examples/cl_command.rs Job::cl(...) + ClMessage walkthrough

Each example reads MAPEPIRE_HOST, MAPEPIRE_USER, and MAPEPIRE_PASSWORD from the environment. Run with cargo run --example <name> (or cargo run --example with_tracing --features tracing for the tracing demo).

Single-connection alternative

If you only need one connection (e.g. a CLI tool or a one-shot script), Job::connect skips the pool entirely:

use mapepire::{DaemonServer, Job, TlsConfig};

# async fn example() -> mapepire::Result<()> {
let server = DaemonServer::builder()
    .host("daemon.example.com")
    .port(8076)
    .user("USER")
    .password(std::env::var("MAPEPIRE_PASSWORD").unwrap())
    .tls(TlsConfig::Verified)
    .build()
    .expect("missing required field");

let job = Job::connect(&server).await?;
let rows = job.execute("SELECT NAME, COUNT FROM SCHEMA.STATS").await?;
let dynamic = rows.into_dynamic().await?;
for row in dynamic {
    let name: String = row.get("NAME")?;
    let count: i64 = row.get("COUNT")?;
    println!("{name}: {count}");
}
# Ok(()) }

Job::connect performs the full TCP → TLS → WebSocket Upgrade → Connect handshake and resolves once the daemon confirms the session.

Cargo features

Feature Default Purpose
rustls-tls on Pure-Rust TLS via rustls (target for v0.2 transport)
native-tls off OS-platform TLS via native-tls (alternate v0.2 backend)
insecure-tls off Compile-time gate for TlsConfig::Insecure (skip server-cert validation; never use in production)
serde-config off DaemonServerSpec DTO for loading from config files (TOML/YAML/JSON via consumer's choice of parser)
tracing off tracing span instrumentation on every public dispatch entry point (Job::execute, Pool::execute, Reserved::*). Per-pool ParameterLogging governs whether parameter values appear on spans.
metrics off metrics facade integration. Counters / gauges / histograms documented at mapepire::observability.

A compile_error! guard fires when neither rustls-tls nor native-tls is enabled — disabling default features requires an explicit alternate TLS backend selection.

Observability (optional)

Enable tracing and/or metrics features for production observability:

[dependencies]
mapepire = { version = "0.4", features = ["rustls-tls", "tracing", "metrics"] }
tracing-subscriber = "0.3"
metrics-exporter-prometheus = "0.15"
# fn install() -> Result<(), Box<dyn std::error::Error>> {
// Tracing — fmt subscriber to stderr.
tracing_subscriber::fmt::init();

// Metrics — Prometheus exporter on :9000/metrics.
metrics_exporter_prometheus::PrometheusBuilder::new().install()?;
# Ok(()) }

Once installed, every Job::execute / Pool::execute / Pool::acquire / Reserved::* call emits the relevant spans and metrics. Both features are zero-cost when disabled.

The metric-name contract is documented at mapepire::observability and is SemVer-stable — names won't be renamed without a major bump.

Roadmap

  • v0.1 — protocol foundation (done).
  • v0.2 — transport, Job::connect, integration tests (done).
  • v0.3Pool with deadpool, Reserved for transactions, public Executor / FromRow traits, diagnostic methods carried over from v0.2 (done).
  • v0.4tracing and metrics feature flags; idle_timeout enforcement; rollback_on_drop tightened to only-if-in-tx; registry-backed routing fast path (done).
  • v1.0 — examples, real-IBM-i CI, donation proposal to the Mapepire-IBMi GitHub org.

Documentation

  • AGENTS.md — contributor and AI-assistant guide (architecture, coding standards, security invariants, MSRV policy)
  • CONTRIBUTING.md — how to open a PR
  • SECURITY.md — vulnerability reporting
  • Makefilemake help lists all dev tasks

License

Dual-licensed under either of:

at your option. By contributing, you agree your contribution will be dual-licensed as above.