nene
nene is a command-line tool to generate Rust code for Google Cloud Spanner.
nene uses database schema to generate code by using Information Schema. nene runs SQL queries against tables in INFORMATION_SCHEMA to fetch metadata for a database, and applies the metadata to Go templates to generate code/models to acccess Cloud Spanner.

Installation
cargo install nene
Usage
export RUST_LOG=info
export SPANNER_DSN=projects/local-project/instances/test-instance/databases/local-database
export SPANNER_EMULATOR_HOST=localhost:9010
mkdir ./gen
nene -o ./gen
-
-i
- template directory.
- see template directory structure
- if not specified default template are used.
-
-o
- output directory
- default directory is
./gen
Generated file with default template
Default template generates the files for google-cloud-spanner.
use google_cloud_googleapis::spanner::v1::Mutation;
use google_cloud_gax::grpc::Status;
use google_cloud_spanner::client::{RunInTxError, TxError};
use google_cloud_spanner::key::Key;
use google_cloud_spanner::mutation::{
delete, insert_or_update_struct, insert_struct, replace_struct, update_struct,
};
use google_cloud_spanner::reader::AsyncIterator;
use google_cloud_spanner::row::{Error as RowError, Row, Struct, TryFromStruct};
use google_cloud_spanner::statement::{Kinds, Statement, ToKind, ToStruct, Types};
use google_cloud_spanner::transaction::Transaction;
use google_cloud_spanner::transaction::CallOptions;
use google_cloud_spanner::value::CommitTimestamp;
use std::convert::TryFrom;
pub const TABLE_NAME: &str = "User";
pub const COLUMN_USER_ID: &str = "UserId";
pub const COLUMN_PREMIUM: &str = "Premium";
pub const COLUMN_UPDATED_AT: &str = "UpdatedAt";
pub struct User {
pub user_id: String,
pub premium: bool,
pub updated_at: chrono::DateTime<chrono::Utc>,
}
impl User {
pub fn insert(&self) -> Mutation {
insert_struct(TABLE_NAME, &self)
}
pub fn update(&self) -> Mutation {
update_struct(TABLE_NAME, &self)
}
pub fn replace(&self) -> Mutation {
replace_struct(TABLE_NAME, &self)
}
pub fn insert_or_update(&self) -> Mutation {
insert_or_update_struct(TABLE_NAME, &self)
}
pub fn delete(&self) -> Mutation {
delete(TABLE_NAME, Key::key(&self.user_id))
}
pub async fn find_by_pk(
tx: &mut Transaction, user_id: &String, options: Option<CallOptions>
) -> Result<Option<Self>, RunInTxError> {
let mut stmt = Statement::new("SELECT * From User WHERE UserId = @UserId");
stmt.add_param(COLUMN_USER_ID, user_id);
let mut rows = Self::read_by_statement(tx, stmt, options).await?;
if !rows.is_empty() {
Ok(rows.pop())
} else {
Ok(None)
}
}
pub async fn read_by_statement(
tx: &mut Transaction,
stmt: Statement,
options: Option<CallOptions>
) -> Result<Vec<Self>, RunInTxError> {
let mut reader = tx.query(stmt).await?;
if options.is_some() {
reader.set_call_options(options.unwrap());
}
let mut result = vec![];
while let Some(row) = reader.next().await? {
let data = Self::try_from(row)?;
result.push(data)
}
Ok(result)
}
}
impl ToStruct for User {
fn to_kinds(&self) -> Kinds {
vec![
(COLUMN_USER_ID, self.user_id.to_kind()),
(COLUMN_PREMIUM, self.premium.to_kind()),
(COLUMN_UPDATED_AT, CommitTimestamp::new().to_kind()),
]
}
fn get_types() -> Types {
vec![
(COLUMN_USER_ID, String::get_type()),
(COLUMN_PREMIUM, bool::get_type()),
(COLUMN_UPDATED_AT, CommitTimestamp::get_type()),
]
}
}
impl TryFromStruct for User {
fn try_from_struct(s: Struct<'_>) -> Result<Self, RowError> {
Ok(User {
user_id: s.column_by_name(COLUMN_USER_ID)?,
premium: s.column_by_name(COLUMN_PREMIUM)?,
updated_at: s.column_by_name(COLUMN_UPDATED_AT)?,
})
}
}
impl TryFrom<Row> for User {
type Error = RowError;
fn try_from(row: Row) -> Result<Self, RowError> {
Ok(User {
user_id: row.column_by_name(COLUMN_USER_ID)?,
premium: row.column_by_name(COLUMN_PREMIUM)?,
updated_at: row.column_by_name(COLUMN_UPDATED_AT)?,
})
}
}