9 个版本 (破坏性)

0.7.0 2024年5月2日
0.6.0 2023年7月10日
0.5.0 2023年2月24日
0.4.0 2023年1月11日
0.1.0 2021年12月6日

#2132 in Web 编程

Download history 146/week @ 2024-04-26 35/week @ 2024-05-03 7/week @ 2024-05-17 3/week @ 2024-05-24

每月560次下载

MIT 许可证

35KB
527

nene

nene 是一个用于生成 Google Cloud Spanner 的 Rust 代码的命令行工具。
nene 通过使用 Information Schema 生成代码,利用数据库模式。它对 INFORMATION_SCHEMA 中的表运行 SQL 查询以获取数据库的元数据,并将元数据应用于 Go 模板以生成访问 Cloud Spanner 的代码/模型。

crates.io CI

安装

cargo install nene

用法

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

    • 模板目录。
    • 请参阅模板目录 结构
    • 如果没有指定,则使用默认模板。
  • -o

    • 输出目录
    • 默认目录为 ./gen
  • -j

    • 添加 serde::Serializeserde::Deserialize
  • -d

    • 实现 Default 特性

使用默认模板生成的文件

默认模板为 google-cloud-spanner 生成文件。

// 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<i64>,
  #[spanner(name = "NotNullFloat64")]
  pub not_null_float_64: f64,
  #[spanner(name = "NullableFloat64")]
  pub nullable_float_64: Option<f64>,
  #[spanner(name = "NotNullBool")]
  pub not_null_bool: bool,
  #[spanner(name = "NullableBool")]
  pub nullable_bool: Option<bool>,
  #[spanner(name = "NotNullByteArray")]
  pub not_null_byte_array: Vec<u8>,
  #[spanner(name = "NullableByteArray")]
  pub nullable_byte_array: Option<Vec<u8>>,
  #[spanner(name = "NotNullNumeric")]
  pub not_null_numeric: google_cloud_spanner::bigdecimal::BigDecimal,
  #[spanner(name = "NullableNumeric")]
  pub nullable_numeric: Option<google_cloud_spanner::bigdecimal::BigDecimal>,
  #[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<time::OffsetDateTime>,
  #[spanner(name = "NotNullDate")]
  pub not_null_date: time::Date,
  #[spanner(name = "NullableDate")]
  pub nullable_date: Option<time::Date>,
  #[spanner(name = "NotNullArray")]
  pub not_null_array: Vec<i64>,
  #[spanner(name = "NullableArray")]
  pub nullable_array: Option<Vec<i64>>,
  #[spanner(name = "NullableString")]
  pub nullable_string: Option<String>,
  #[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<CallOptions>
  ) -> Result<Option<Self>, 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<T: TryFrom<Row, Error = RowError>>(
  tx: &mut Transaction,
  stmt: Statement,
  options: Option<CallOptions>,
) -> Result<Vec<T>, 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)
}

依赖关系

~28–38MB
~676K SLoC