use crate::i18n::Translator;
use crate::sql::{Auto, ExecError, Pool};
use crate::Model;
#[derive(Model, Debug, Clone, serde::Serialize)]
#[rustango(table = "rustango_translations", managed = false, scope = "registry")]
pub struct Translation {
#[rustango(primary_key)]
pub id: Auto<i64>,
#[rustango(max_length = 16)]
pub locale: String,
#[rustango(max_length = 200)]
pub key: String,
pub value: String,
#[rustango(max_length = 200, default = "")]
pub updated_by: String,
#[rustango(auto_now_add)]
pub created_at: Auto<chrono::DateTime<chrono::Utc>>,
#[rustango(auto_now)]
pub updated_at: Auto<chrono::DateTime<chrono::Utc>>,
}
fn ddl(dialect: &dyn crate::sql::Dialect) -> String {
let ts = dialect.timestamp_now_column();
match dialect.name() {
"mysql" => format!(
r"
CREATE TABLE IF NOT EXISTS `rustango_translations` (
`id` BIGINT AUTO_INCREMENT PRIMARY KEY,
`locale` VARCHAR(16) NOT NULL,
`key` VARCHAR(200) NOT NULL,
`value` TEXT NOT NULL,
`updated_by` VARCHAR(200) NOT NULL DEFAULT '',
`created_at` {ts},
`updated_at` {ts},
CONSTRAINT `rustango_translations_locale_key_uq` UNIQUE (`locale`, `key`)
);
"
),
"sqlite" => format!(
r#"
CREATE TABLE IF NOT EXISTS "rustango_translations" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"locale" TEXT NOT NULL,
"key" TEXT NOT NULL,
"value" TEXT NOT NULL,
"updated_by" TEXT NOT NULL DEFAULT '',
"created_at" {ts},
"updated_at" {ts},
CONSTRAINT "rustango_translations_locale_key_uq" UNIQUE ("locale", "key")
);
"#
),
_ => format!(
r#"
CREATE TABLE IF NOT EXISTS "rustango_translations" (
"id" BIGSERIAL PRIMARY KEY,
"locale" VARCHAR(16) NOT NULL,
"key" VARCHAR(200) NOT NULL,
"value" TEXT NOT NULL,
"updated_by" VARCHAR(200) NOT NULL DEFAULT '',
"created_at" {ts},
"updated_at" {ts},
CONSTRAINT "rustango_translations_locale_key_uq" UNIQUE ("locale", "key")
);
"#
),
}
}
pub async fn ensure_table_pool(pool: &Pool) -> Result<(), sqlx::Error> {
crate::sql::run_ddl_idempotent(pool, &ddl(pool.dialect())).await
}
pub async fn all_pool(pool: &Pool) -> Result<Vec<Translation>, ExecError> {
use crate::sql::FetcherPool as _;
Translation::objects().fetch(pool).await
}
pub async fn upsert_pool(
pool: &Pool,
locale: &str,
key: &str,
value: &str,
updated_by: &str,
) -> Result<(), ExecError> {
let row = Translation {
id: Auto::Unset,
locale: locale.to_owned(),
key: key.to_owned(),
value: value.to_owned(),
updated_by: updated_by.to_owned(),
created_at: Auto::Unset,
updated_at: Auto::Unset,
};
Translation::bulk_upsert_pool(
&[row],
&["locale", "key"],
&["value", "updated_by", "updated_at"],
pool,
)
.await
}
pub async fn seed_from_translator_pool(
pool: &Pool,
translator: &Translator,
) -> Result<usize, ExecError> {
let rows: Vec<Translation> = translator
.entries()
.into_iter()
.map(|(locale, key, value)| Translation {
id: Auto::Unset,
locale,
key,
value,
updated_by: String::new(),
created_at: Auto::Unset,
updated_at: Auto::Unset,
})
.collect();
let n = rows.len();
if n > 0 {
Translation::bulk_insert_or_ignore_pool(&rows, pool).await?;
}
Ok(n)
}
pub async fn delete_key_pool(pool: &Pool, key: &str) -> Result<u64, ExecError> {
use crate::core::Model as _; use crate::core::{DeleteQuery, Filter, Op, SqlValue, WhereExpr};
crate::sql::delete_pool(
pool,
&DeleteQuery {
model: Translation::SCHEMA,
where_clause: WhereExpr::and_predicates(vec![Filter {
column: "key",
op: Op::Eq,
value: SqlValue::from(key),
}]),
},
)
.await
}
pub async fn delete_override(pool: &Pool, locale: &str, key: &str) -> Result<u64, ExecError> {
use crate::core::Model as _;
use crate::core::{DeleteQuery, Filter, Op, SqlValue, WhereExpr};
crate::sql::delete_pool(
pool,
&DeleteQuery {
model: Translation::SCHEMA,
where_clause: WhereExpr::and_predicates(vec![
Filter {
column: "locale",
op: Op::Eq,
value: SqlValue::from(locale),
},
Filter {
column: "key",
op: Op::Eq,
value: SqlValue::from(key),
},
]),
},
)
.await
}
pub async fn refresh_overrides_pool(
translator: &Translator,
pool: &Pool,
) -> Result<usize, ExecError> {
let rows = all_pool(pool).await?;
let n = rows.len();
translator.load_overrides(rows.into_iter().map(|t| (t.locale, t.key, t.value)));
Ok(n)
}