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 CI

Installation

docker pull ghcr.io/yoshidan/nene/nene:0.1.0

Usage

bash services: nene: image: ghcr.io/yoshidan/nene/nene:0.1.0 command: -o /model #-i /template volumes: - ./model:/model # - ./template:/template environment: RUST_LOG: info 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 SPANNER_EMULATOR_HOST: host.docker.internal:9010

Generated file with default template

Default template generates the files for google-cloud-spanner.

```rust // DON'T EDIT. this code is generated by nene. use googlecloudgoogleapis::spanner::v1::Mutation; use googlecloudgoogleapis::Status; use googlecloudspanner::client::{RunInTxError, TxError}; use googlecloudspanner::key::Key; use googlecloudspanner::mutation::{ delete, insertorupdatestruct, insertstruct, replacestruct, updatestruct, }; use googlecloudspanner::reader::AsyncIterator; use googlecloudspanner::row::{Error as RowError, Row, Struct, TryFromStruct}; use googlecloudspanner::statement::{Kinds, Statement, ToKind, ToStruct, Types}; use googlecloudspanner::transaction::Transaction; use googlecloudspanner::value::CommitTimestamp; use std::convert::TryFrom;

pub const TABLENAME: &str = "User"; pub const COLUMNUSERID: &str = "UserId"; pub const COLUMNPREMIUM: &str = "Premium"; pub const COLUMNUPDATEDAT: &str = "UpdatedAt";

pub struct User { pub userid: String, pub premium: bool, pub updatedat: chrono::DateTime, }

impl User { pub fn insert(&self) -> Mutation { insertstruct(TABLENAME, &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
) -> 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).await?;
     if !rows.is_empty() {
        Ok(rows.pop())
     } else {
        Ok(None)
     }
}

pub async fn read_by_statement(
    tx: &mut Transaction,
    stmt: Statement,
) -> Result<Vec<Self>, RunInTxError> {
    let mut reader = tx.query(stmt).await?;
    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 tokinds(&self) -> Kinds { vec![ (COLUMNUSERID, self.userid.tokind()), (COLUMNPREMIUM, self.premium.tokind()), (COLUMNUPDATEDAT, CommitTimestamp::new().tokind()), ] }

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 tryfromstruct(s: Struct<'>) -> Result { Ok(User { userid: s.columnbyname(COLUMNUSERID)?, premium: s.columnbyname(COLUMNPREMIUM)?, updatedat: s.columnbyname(COLUMNUPDATEDAT)?, }) } }

impl TryFrom for User { type Error = RowError; fn tryfrom(row: Row) -> Result { Ok(User { userid: row.columnbyname(COLUMNUSERID)?, premium: row.columnbyname(COLUMNPREMIUM)?, updatedat: row.columnbyname(COLUMNUPDATEDAT)?, }) } } ```