use futures::StreamExt;
use hyperdb_api::{
AsyncConnection, Connection, CreateMode, FromRow, HyperProcess, Parameters, Result, RowAccessor,
};
use hyperdb_api_derive::FromRow;
const QUERY: &str = "SELECT id, name, price, in_stock FROM products ORDER BY id";
fn main() -> Result<()> {
std::fs::create_dir_all("test_results")?;
let mut params = Parameters::new();
params.set("log_dir", "test_results");
let hyper = HyperProcess::new(None, Some(¶ms))?;
let conn = Connection::new(
&hyper,
"test_results/row_mapping_forms.hyper",
CreateMode::CreateAndReplace,
)?;
seed_products(&conn)?;
form1_manual_streaming(&conn)?;
form2_named_access(&conn)?;
form3_manual_from_row(&conn)?;
form4_derive_from_row(&conn)?;
form5_streaming_from_row(&conn)?;
drop(conn);
let endpoint = hyper.require_endpoint()?.to_string();
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.map_err(|e| hyperdb_api::Error::config(format!("failed to build Tokio runtime: {e}")))?
.block_on(form5_streaming_from_row_async(&endpoint))?;
Ok(())
}
fn seed_products(conn: &Connection) -> Result<()> {
conn.execute_command(
"CREATE TABLE products (
id INT NOT NULL,
name TEXT NOT NULL,
price DOUBLE PRECISION NOT NULL,
in_stock BOOLEAN NOT NULL
)",
)?;
conn.execute_command(
"INSERT INTO products VALUES
(1, 'Widget', 9.99, true),
(2, 'Gadget', 19.95, false),
(3, 'Gizmo', 4.50, true),
(4, 'Doohickey', 14.00, true)",
)?;
Ok(())
}
fn form1_manual_streaming(conn: &Connection) -> Result<()> {
println!("== Form 1 — manual streaming (execute_query + next_chunk) ==");
let mut result = conn.execute_query(QUERY)?;
while let Some(chunk) = result.next_chunk()? {
for row in &chunk {
let id: Option<i32> = row.get(0);
let name: Option<String> = row.get(1);
let price: Option<f64> = row.get(2);
let in_stock: Option<bool> = row.get(3);
print_row(
id.unwrap_or(-1),
&name.unwrap_or_default(),
price.unwrap_or(0.0),
in_stock.unwrap_or(false),
);
}
}
println!();
Ok(())
}
fn form2_named_access(conn: &Connection) -> Result<()> {
println!("== Form 2 — named access (fetch_all + Row::get_by_name) ==");
let rows = conn.fetch_all(QUERY)?;
for row in &rows {
let id: i32 = row.get_by_name("id")?;
let name: String = row.get_by_name("name")?;
let price: f64 = row.get_by_name("price")?;
let in_stock: bool = row.get_by_name("in_stock")?;
print_row(id, &name, price, in_stock);
}
println!();
Ok(())
}
#[derive(Debug)]
struct ProductManual {
id: i32,
name: String,
price: f64,
in_stock: bool,
}
impl FromRow for ProductManual {
fn from_row(row: RowAccessor<'_>) -> Result<Self> {
Ok(ProductManual {
id: row.get("id")?,
name: row.get("name")?,
price: row.get("price")?,
in_stock: row.get("in_stock")?,
})
}
}
fn form3_manual_from_row(conn: &Connection) -> Result<()> {
println!("== Form 3 — manual FromRow impl (fetch_all_as) ==");
let products: Vec<ProductManual> = conn.fetch_all_as(QUERY)?;
for p in &products {
print_row(p.id, &p.name, p.price, p.in_stock);
}
println!();
Ok(())
}
#[derive(Debug, FromRow)]
struct ProductDerived {
id: i32,
name: String,
price: f64,
in_stock: bool,
}
fn form4_derive_from_row(conn: &Connection) -> Result<()> {
println!("== Form 4 — #[derive(FromRow)] (fetch_all_as) ==");
let products: Vec<ProductDerived> = conn.fetch_all_as(QUERY)?;
for p in &products {
print_row(p.id, &p.name, p.price, p.in_stock);
}
println!();
Ok(())
}
fn form5_streaming_from_row(conn: &Connection) -> Result<()> {
println!("== Form 5 — streaming FromRow (stream_as, constant memory) ==");
for row_result in conn.stream_as::<ProductDerived>(QUERY)? {
let p = row_result?;
print_row(p.id, &p.name, p.price, p.in_stock);
}
println!();
Ok(())
}
async fn form5_streaming_from_row_async(endpoint: &str) -> Result<()> {
println!("== Form 5 (async) — AsyncConnection::stream_as (impl Stream) ==");
let conn = AsyncConnection::connect(
endpoint,
"test_results/row_mapping_forms.hyper",
CreateMode::DoNotCreate,
)
.await?;
{
let stream = conn.stream_as::<ProductDerived>(QUERY);
tokio::pin!(stream);
while let Some(row_result) = stream.next().await {
let p = row_result?;
print_row(p.id, &p.name, p.price, p.in_stock);
}
}
conn.close().await?;
println!();
Ok(())
}
fn print_row(id: i32, name: &str, price: f64, in_stock: bool) {
println!("{id:>2} {name:<10} ${price:.2} in_stock={in_stock}");
}