# 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. [![crates.io](https://img.shields.io/crates/v/nene.svg)](https://crates.io/crates/nene) ![CI](https://github.com/yoshidan/nene/workflows/CI/badge.svg?branch=main) ## Installation ``` cargo install nene ``` ## Usage ```bash export RUST_LOG=info export SPANNER_DSN=projects/local-project/instances/test-instance/databases/local-database # if you don't use emulator use GOOGLE_APPLICATION_CREDENTIALS instead of SPANNER_EMULATOR_HOST export SPANNER_EMULATOR_HOST=localhost:9010 mkdir ./gen nene -o ./gen -j -d ``` * -i - template directory. - see template directory [structure](./src/default) - if not specified default template are used. * -o - output directory - default directory is `./gen` * -j - add `serde::Serialize` and `serde::Deserialize` * -d - implements `Default` trait ### Generated file with default template Default template generates the files for [google-cloud-spanner](https://github.com/yoshidan/google-cloud-rust/tree/main/spanner). ```rust // DON'T EDIT. this code is generated by nene. use google_cloud_googleapis::spanner::v1::Mutation; use google_cloud_spanner::client::{Error}; 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}; use google_cloud_spanner::statement::Statement; use google_cloud_spanner::transaction::Transaction; use google_cloud_spanner::transaction::CallOptions; use google_cloud_spanner_derive::Table; use std::convert::TryFrom; pub const TABLE_NAME: &str = "User"; pub const COLUMN_USER_ID: &str = "UserId"; pub const COLUMN_NOT_NULL_INT_64: &str = "NotNullINT64"; pub const COLUMN_NULLABLE_INT_64: &str = "NullableINT64"; pub const COLUMN_NOT_NULL_FLOAT_64: &str = "NotNullFloat64"; pub const COLUMN_NULLABLE_FLOAT_64: &str = "NullableFloat64"; pub const COLUMN_NOT_NULL_BOOL: &str = "NotNullBool"; pub const COLUMN_NULLABLE_BOOL: &str = "NullableBool"; pub const COLUMN_NOT_NULL_BYTE_ARRAY: &str = "NotNullByteArray"; pub const COLUMN_NULLABLE_BYTE_ARRAY: &str = "NullableByteArray"; pub const COLUMN_NOT_NULL_NUMERIC: &str = "NotNullNumeric"; pub const COLUMN_NULLABLE_NUMERIC: &str = "NullableNumeric"; pub const COLUMN_NOT_NULL_TIMESTAMP: &str = "NotNullTimestamp"; pub const COLUMN_NULLABLE_TIMESTAMP: &str = "NullableTimestamp"; pub const COLUMN_NOT_NULL_DATE: &str = "NotNullDate"; pub const COLUMN_NULLABLE_DATE: &str = "NullableDate"; pub const COLUMN_NOT_NULL_ARRAY: &str = "NotNullArray"; pub const COLUMN_NULLABLE_ARRAY: &str = "NullableArray"; pub const COLUMN_NULLABLE_STRING: &str = "NullableString"; pub const COLUMN_UPDATED_AT: &str = "UpdatedAt"; #[derive(Debug,Clone,Table,serde::Serialize,serde::Deserialize)] pub struct User { #[spanner(name = "UserId")] pub user_id: String, #[spanner(name = "NotNullINT64")] pub not_null_int_64: i64, #[spanner(name = "NullableINT64")] pub nullable_int_64: Option, #[spanner(name = "NotNullFloat64")] pub not_null_float_64: f64, #[spanner(name = "NullableFloat64")] pub nullable_float_64: Option, #[spanner(name = "NotNullBool")] pub not_null_bool: bool, #[spanner(name = "NullableBool")] pub nullable_bool: Option, #[spanner(name = "NotNullByteArray")] pub not_null_byte_array: Vec, #[spanner(name = "NullableByteArray")] pub nullable_byte_array: Option>, #[spanner(name = "NotNullNumeric")] pub not_null_numeric: google_cloud_spanner::bigdecimal::BigDecimal, #[spanner(name = "NullableNumeric")] pub nullable_numeric: Option, #[serde(with = "time::serde::rfc3339")] #[spanner(name = "NotNullTimestamp")] pub not_null_timestamp: time::OffsetDateTime, #[serde(default,with = "time::serde::rfc3339::option")] #[spanner(name = "NullableTimestamp")] pub nullable_timestamp: Option, #[spanner(name = "NotNullDate")] pub not_null_date: time::Date, #[spanner(name = "NullableDate")] pub nullable_date: Option, #[spanner(name = "NotNullArray")] pub not_null_array: Vec, #[spanner(name = "NullableArray")] pub nullable_array: Option>, #[spanner(name = "NullableString")] pub nullable_string: Option, #[serde(with = "time::serde::rfc3339")] #[spanner(name = "UpdatedAt",commitTimestamp)] pub updated_at: time::OffsetDateTime, } impl Default for User { fn default() -> Self { Self { user_id: Default::default(), not_null_int_64: Default::default(), nullable_int_64: Default::default(), not_null_float_64: Default::default(), nullable_float_64: Default::default(), not_null_bool: Default::default(), nullable_bool: Default::default(), not_null_byte_array: Default::default(), nullable_byte_array: Default::default(), not_null_numeric: Default::default(), nullable_numeric: Default::default(), not_null_timestamp: time::OffsetDateTime::now_utc(), nullable_timestamp: Default::default(), not_null_date: time::OffsetDateTime::now_utc().date(), nullable_date: Default::default(), not_null_array: Default::default(), nullable_array: Default::default(), nullable_string: Default::default(), updated_at: time::OffsetDateTime::now_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::new(&self.user_id)) } pub async fn find_by_pk( tx: &mut Transaction, user_id: &str, options: Option ) -> Result, Error> { let mut stmt = Statement::new("SELECT * From User WHERE UserId = @UserId"); stmt.add_param(COLUMN_USER_ID, &user_id); let mut rows = read_by_statement(tx, stmt, options).await?; if !rows.is_empty() { Ok(rows.pop()) } else { Ok(None) } } } async fn read_by_statement>( tx: &mut Transaction, stmt: Statement, options: Option, ) -> Result, Error> { 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? { result.push(row.try_into()?); } Ok(result) } ```