Expand description
§Rust gRPC Client Driver for Stargate
This crate provides a high-level async Rust driver for querying Stargate.
§Features
- All of the Stargate gRPC protocol messages exposed as Rust structures and enums
- Token-based authentication
- Asynchronous querying
- Query builder with easy binding of variables by names or positions
- Optional compile-time type-checking of query bind values
- Easy conversions between gRPC value types and common Rust types; support for primitive types, lists, maps, tuples and user-defined-types, with arbitrary nesting levels
- Result set paging
§Usage
Add required dependencies.
[dependencies]
stargate-grpc = "0.1"
tokio = { version = "1", features = ["full"]}§Establishing the connection
The main structure that provides the interface to Stargate is StargateClient.
The simplest way to obtain an instance is to use the provided
builder:
use std::str::FromStr;
use stargate_grpc::client::{default_tls_config, AuthToken, StargateClient};
let uri = "http://localhost:8090/"; // Stargate URI
let token = "00000000-0000-0000-0000-000000000000"; // Stargate authentication token
let token = AuthToken::from_str(token)?;
let mut client = StargateClient::builder()
.uri(uri)?
.auth_token(token)
.tls(Some(default_tls_config()?)) // optionally to enable TLS
.connect()
.await?;If you want to control the properties of the connection which are not exposed by the builder,
add tonic to the dependencies of the project and create
the connection manually. Then use StargateClient::with_auth to wrap the connection and
the authentication token:
use std::time::Duration;
let channel = tonic::transport::Endpoint::new(uri)?
.connect_timeout(Duration::from_secs(30))
.tcp_nodelay(true)
.connect().await?;
let mut client = StargateClient::with_auth(channel, token);§Querying
Call Query::builder to set a CQL string, bind query arguments
set query parameters and finally produce a Query:
use stargate_grpc::{Consistency, Query};
let query = Query::builder()
.keyspace("test") // set the keyspace the query applies to
.consistency(Consistency::LocalQuorum) // set consistency level
.query("SELECT * FROM users WHERE id = :id") // set CQL query text (required)
.bind_name("id", 1000) // bind :id to 1000
.build(); // build the QueryRun the query and wait for its results:
use stargate_grpc::ResultSet;
let response = client.execute_query(query).await?; // send the query and wait for gRPC response
let result_set: ResultSet = response.try_into()?; // convert the response into ResultSetIf you need to send more than one query in a single request, create a Batch.
All queries in the batch will share the same parameters, such as
keyspace, consistency level or timestamp. Send the batch for execution with
StargateClient::execute_batch.
§Processing the result set
A ResultSet comes back as a collection of rows. A Row can be easily unpacked
into a tuple:
for row in result_set.rows {
let (login, emails): (String, Vec<String>) = row.try_into()?;
// ...
}It is also possible to read each field separately and convert it to desired type:
for mut row in result_set.rows {
let login: String = row.try_take(0)?;
let emails: Vec<String> = row.try_take(1)?;
// ...
}Alternativelly, you can convert a whole Row into a struct with a mapper obtained
from ResultSet::mapper:
use stargate_grpc::{ResultSet, TryFromRow};
#[derive(TryFromRow)]
struct User {
login: String,
emails: Vec<String>
}
let mapper = result_set.mapper()?;
for row in result_set.rows {
let user: User = mapper.try_unpack(row)?;
// ...
}§Representation of values
The values bound in queries and the values received in the Rows of a ResultSet
are internally represented by struct Value. A Value wraps an enum that can
hold one of many data types. Value provides factory functions that produce values
of desired type, so it is easy to construct them.
use stargate_grpc::Value;
let bool = Value::boolean(true);
let int = Value::bigint(1);
let double = Value::double(1.0);
let string = Value::string("stargate");
let list = Value::list(vec![Value::bigint(1), Value::bigint(2), Value::bigint(3)]);
let map = Value::map(vec![("key1", Value::bigint(1)), ("key2", Value::bigint(2))]);List or maps can hold values of different types:
let heterogeneous_list = vec![Value::bigint(1), Value::double(3.14)];Values can be used in calls to bind or bind_name used when building queries or batches:
use stargate_grpc::{Query, Value};
let query = Query::builder()
.query("SELECT login, emails FROM users WHERE id = :id")
.bind_name("id", Value::bigint(1000))
.build();A Row is represented by a vector of Values:
use std::convert::TryInto;
use stargate_grpc::{Row, Value};
let row = Row { values: vec![Value::bigint(1), Value::double(3.14)] };Values can be converted to and from other commonly used Rust types.
For more examples, refer to the documentation of modules from_value and into_value.
§Working with UUIDs
This crate provides only a very lightweight representation of UUIDs: proto::Uuid.
A UUID is internally represented as an vector of bytes.
That struct does not provide any functions to generate nor manipulate the
UUID value, however, it should be fairly easy to convert to from other UUID representations.
To get support for conversions from and to
uuid::UUID,
bring uuid on the dependency list and enable feature uuid.
[dependencies]
uuid = "0.8
stargate-grpc = { version = "0.1", features = ["uuid"] }§Working with times, dates and timestamps
This crate doesn’t define its own fully-fledged structures for representing dates, times and timestamps. It allows an easy integration with external structures instead.
A time value is internally represented as an u64 number of nanoseconds elapsed
since midnight. Hence, Value::time(0) denotes midnight.
A date is internally represented as an u32 number where value 2^31 denotes Unix Epoch.
For convenience and compatibility with most other date representations, values are convertible
to and from i32 type where 0 denotes the Unix Epoch. Therefore the Unix Epoch can be simply
written as Value::date(0) which is equivalent to Value::raw_date(1 << 31).
A timestamp is internally represented as an i64 number of milliseconds
elapsed since Unix epoch. Timestamps can be negative.
Using integers to represent dates is error-prone, therefore
this library comes with conversions
from higher-level structures like SystemTime:
use std::time::SystemTime;
use stargate_grpc::Value;
let unix_epoch_1 = Value::timestamp(SystemTime::UNIX_EPOCH);
let unix_epoch_2 = Value::timestamp(0);
assert_eq!(unix_epoch_1, unix_epoch_2);More time related features are provided by an optional chrono feature.
If you enable chrono feature, you get conversions for
chrono::Date and
chrono::DateTime.
[dependencies]
chrono = "0.4"
stargate-grpc = { version = "0.1", features = ["chrono"] }§Mapping Rust structs to user defined types
Feature
stargate-grpc-derive
allows to generate conversions between Values and your Rust structs by adding
the #[derive(IntoValue, TryFromValue)] attribute on top of a struct definition.
Re-exports§
pub use client::AuthToken;pub use client::StargateClient;pub use from_value::TryFromValue;pub use into_value::DefaultCqlType;pub use into_value::IntoValue;pub use proto::Batch;pub use proto::Consistency;pub use proto::Query;pub use proto::ResultSet;pub use proto::Row;pub use proto::Value;
Modules§
- client
- Enhances the automatically generated gRPC Stargate client with token-based authentication.
- error
- Errors returned from failed attempts to convert data.
- from_
value - Automatic conversions from
Valueto standard Rust types. - into_
value - Automatic conversions from standard Rust types to
Value. - proto
- Structures automatically generated from gRPC protocol definition files located in
api/. - query
- Utilities for building queries.
- result
- Automatic data type conversions and utilities useful for processing query results.
- types
- Structs for describing the types of gRPC data values
Structs§
- KeyValue
- Holds a key and a value pair; used in map representation.