Skip to content
This repository has been archived by the owner on Oct 18, 2023. It is now read-only.

Commit

Permalink
libsql-client: introduce into_result_set()
Browse files Browse the repository at this point in the history
It's a convenience function that translates QueryResult
to anyhow::Result, which makes it work with the `?` operator.
  • Loading branch information
psarna committed Feb 16, 2023
1 parent 77d9d51 commit 1a69905
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 70 deletions.
2 changes: 1 addition & 1 deletion packages/rust/libsql-client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "libsql-client"
version = "0.5.7"
version = "0.5.9"
edition = "2021"
license = "Apache-2.0"
description = "HTTP-based client for libSQL and sqld"
Expand Down
59 changes: 26 additions & 33 deletions packages/rust/libsql-client/examples/connect_from_env.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,30 @@
use libsql_client::{connect, Connection, QueryResult, Statement, Value};
use anyhow::Result;
use libsql_client::{connect, params, Connection, QueryResult, ResultSet, Statement};
use rand::prelude::SliceRandom;

fn result_to_string(result: QueryResult) -> String {
fn result_to_string(query_result: QueryResult) -> Result<String> {
let mut ret = String::new();
match result {
QueryResult::Error((msg, _)) => return format!("Error: {msg}"),
QueryResult::Success((result, _)) => {
for column in &result.columns {
ret += &format!("| {column:16} |");
}
ret += "\n| -------------------------------------------------------- |\n";
for row in result.rows {
for column in &result.columns {
ret += &format!("| {:16} |", row.cells[column].to_string());
}
ret += "\n";
}
let ResultSet { columns, rows } = query_result.into_result_set()?;
for column in &columns {
ret += &format!("| {column:16} |");
}
ret += "\n| -------------------------------------------------------- |\n";
for row in rows {
for column in &columns {
ret += &format!("| {:16} |", row.cells[column].to_string());
}
};
ret
ret += "\n";
}
Ok(ret)
}

// Bumps a counter for one of the geographic locations picked at random.
async fn bump_counter(db: impl Connection) -> String {
async fn bump_counter(db: impl Connection) -> Result<String> {
// Recreate the tables if they do not exist yet
db.batch([
"CREATE TABLE IF NOT EXISTS counter(country TEXT, city TEXT, value, PRIMARY KEY(country, city)) WITHOUT ROWID",
"CREATE TABLE IF NOT EXISTS coordinates(lat INT, long INT, airport TEXT, PRIMARY KEY (lat, long))"
]).await.ok();
]).await?;

// For demo purposes, let's pick a pseudorandom location
const FAKE_LOCATIONS: &[(&str, &str, &str, f64, f64)] = &[
Expand All @@ -44,6 +41,7 @@ async fn bump_counter(db: impl Connection) -> String {
db.transaction([
Statement::with_params(
"INSERT OR IGNORE INTO counter VALUES (?, ?, 0)",
// Parameters that have a single type can be passed as a regular slice
&[country, city],
),
Statement::with_params(
Expand All @@ -52,29 +50,24 @@ async fn bump_counter(db: impl Connection) -> String {
),
Statement::with_params(
"INSERT OR IGNORE INTO coordinates VALUES (?, ?, ?)",
&[
Value::Real(latitude),
Value::Real(longitude),
airport.into(),
],
// Parameters with different types can be passed to a convenience macro - params!()
params!(latitude, longitude, airport),
),
])
.await
.ok();
.await?;

let counter_response = match db.execute("SELECT * FROM counter").await {
Ok(resp) => resp,
Err(e) => return format!("Error: {e}"),
};
let scoreboard = result_to_string(counter_response);
let counter_response = db.execute("SELECT * FROM counter").await?;
let scoreboard = result_to_string(counter_response)?;
let html = format!("Scoreboard:\n{scoreboard}");
html
Ok(html)
}

#[tokio::main]
async fn main() {
let db = connect().unwrap();
let response = bump_counter(db).await;
let response = bump_counter(db)
.await
.unwrap_or_else(|e| format!("Error: {e}"));
println!(
"Connection parameters: backend={:?} url={:?}\n{response}",
std::env::var("LIBSQL_CLIENT_BACKEND"),
Expand Down
59 changes: 28 additions & 31 deletions packages/rust/libsql-client/examples/select.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,30 @@
use libsql_client::{params, Connection, QueryResult, Statement};
use anyhow::Result;
use libsql_client::{params, Connection, QueryResult, ResultSet, Statement};
use rand::prelude::SliceRandom;

fn result_to_string(result: QueryResult) -> String {
fn result_to_string(query_result: QueryResult) -> Result<String> {
let mut ret = String::new();
match result {
QueryResult::Error((msg, _)) => return format!("Error: {msg}"),
QueryResult::Success((result, _)) => {
for column in &result.columns {
ret += &format!("| {column:16} |");
}
ret += "\n| -------------------------------------------------------- |\n";
for row in result.rows {
for column in &result.columns {
ret += &format!("| {:16} |", row.cells[column].to_string());
}
ret += "\n";
}
let ResultSet { columns, rows } = query_result.into_result_set()?;
for column in &columns {
ret += &format!("| {column:16} |");
}
ret += "\n| -------------------------------------------------------- |\n";
for row in rows {
for column in &columns {
ret += &format!("| {:16} |", row.cells[column].to_string());
}
};
ret
ret += "\n";
}
Ok(ret)
}

// Bumps a counter for one of the geographic locations picked at random.
async fn bump_counter(db: impl Connection) -> String {
async fn bump_counter(db: impl Connection) -> Result<String> {
// Recreate the tables if they do not exist yet
db.batch([
"CREATE TABLE IF NOT EXISTS counter(country TEXT, city TEXT, value, PRIMARY KEY(country, city)) WITHOUT ROWID",
"CREATE TABLE IF NOT EXISTS coordinates(lat INT, long INT, airport TEXT, PRIMARY KEY (lat, long))"
]).await.ok();
]).await?;

// For demo purposes, let's pick a pseudorandom location
const FAKE_LOCATIONS: &[(&str, &str, &str, f64, f64)] = &[
Expand Down Expand Up @@ -57,31 +54,31 @@ async fn bump_counter(db: impl Connection) -> String {
params!(latitude, longitude, airport),
),
])
.await
.ok();
.await?;

let counter_response = match db.execute("SELECT * FROM counter").await {
Ok(resp) => resp,
Err(e) => return format!("Error: {e}"),
};
let scoreboard = result_to_string(counter_response);
let counter_response = db.execute("SELECT * FROM counter").await?;
let scoreboard = result_to_string(counter_response)?;
let html = format!("Scoreboard:\n{scoreboard}");
html
Ok(html)
}

#[tokio::main]
async fn main() {
match libsql_client::reqwest::Connection::connect_from_env() {
Ok(remote_db) => {
let response = bump_counter(remote_db).await;
println!("Remote:\n{response}");
match bump_counter(remote_db).await {
Ok(response) => println!("Remote:\n{response}"),
Err(e) => println!("Remote database query failed: {e}"),
};
}
Err(e) => println!("Failed to fetch from a remote database: {e}"),
}

let mut path_buf = std::env::temp_dir();
path_buf.push("libsql_client_test_db.db");
let local_db = libsql_client::local::Connection::connect(path_buf.as_path()).unwrap();
let response = bump_counter(local_db).await;
println!("Local:\n{response}");
match bump_counter(local_db).await {
Ok(response) => println!("Local:\n{response}"),
Err(e) => println!("Local database query failed: {e}"),
};
}
22 changes: 18 additions & 4 deletions packages/rust/libsql-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,21 @@ pub enum QueryResult {
Success((ResultSet, Meta)),
}

pub fn parse_columns(columns: Vec<serde_json::Value>, result_idx: usize) -> Result<Vec<String>> {
impl QueryResult {
/// Transforms a query result into [`anyhow::Result<ResultSet>`]
/// for convenient chaining with the `?` iterator.
pub fn into_result_set(self) -> Result<ResultSet> {
match self {
QueryResult::Success((result_set, _)) => Ok(result_set),
QueryResult::Error((msg, _)) => Err(anyhow!("Querying database failed: {msg}")),
}
}
}

pub(crate) fn parse_columns(
columns: Vec<serde_json::Value>,
result_idx: usize,
) -> Result<Vec<String>> {
let mut result = Vec::with_capacity(columns.len());
for (idx, column) in columns.into_iter().enumerate() {
match column {
Expand All @@ -74,7 +88,7 @@ pub fn parse_columns(columns: Vec<serde_json::Value>, result_idx: usize) -> Resu
Ok(result)
}

pub fn parse_value(
pub(crate) fn parse_value(
cell: serde_json::Value,
result_idx: usize,
row_idx: usize,
Expand All @@ -98,7 +112,7 @@ pub fn parse_value(
}
}

pub fn parse_rows(
pub(crate) fn parse_rows(
rows: Vec<serde_json::Value>,
columns: &Vec<String>,
result_idx: usize,
Expand Down Expand Up @@ -127,7 +141,7 @@ pub fn parse_rows(
Ok(result)
}

pub fn parse_query_result(result: serde_json::Value, idx: usize) -> Result<QueryResult> {
pub(crate) fn parse_query_result(result: serde_json::Value, idx: usize) -> Result<QueryResult> {
match result {
serde_json::Value::Object(obj) => {
if let Some(err) = obj.get("error") {
Expand Down
2 changes: 1 addition & 1 deletion packages/rust/libsql-client/src/reqwest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ impl Connection {
let user = match std::env::var("LIBSQL_CLIENT_USER") {
Ok(user) => user,
Err(_) => {
return Ok(Connection::connect_from_url(&url::Url::parse(&url)?)?);
return Connection::connect_from_url(&url::Url::parse(&url)?);
}
};
let pass = std::env::var("LIBSQL_CLIENT_PASS").map_err(|_| {
Expand Down

0 comments on commit 1a69905

Please sign in to comment.