// jeeflow-persist —— 移植自 rust jeeflow-persist/src/lib.rs
// DynamicTableWriter + PersistPostInterceptor(ARCHIVE/SYNC,spec/09)+ 元数据 + 表名安全。
///|
/// 字段元数据(动态表列)
pub(all) struct FieldMeta {
mut column_name : String
mut column_type : String
mut display_name : String
mut nullable : Bool
mut default_value : String?
mut permission : Int // 1 只读 / 2 可编辑 / 3 隐藏
}
///|
pub fn FieldMeta::make(column_name : String, column_type : String, display_name : String) -> FieldMeta {
{
column_name,
column_type,
display_name,
nullable: true,
default_value: None,
permission: 2,
}
}
///|
pub fn FieldMeta::is_editable(self : FieldMeta) -> Bool {
self.permission == 2
}
///|
pub fn FieldMeta::is_visible(self : FieldMeta) -> Bool {
self.permission != 3
}
///|
pub(all) struct TableMeta {
mut table_name : String
mut display_name : String
mut fields : Array[FieldMeta]
}
///|
pub fn TableMeta::make(table_name : String, display_name : String) -> TableMeta {
{ table_name, display_name, fields: [] }
}
///|
pub fn TableMeta::add_field(self : TableMeta, field : FieldMeta) -> Unit {
self.fields.push(field)
}
///|
pub fn TableMeta::get_field(self : TableMeta, column_name : String) -> FieldMeta? {
let mut found : FieldMeta? = None
for f in self.fields {
if f.column_name == column_name {
found = Some(f)
}
}
found
}
///|
/// IDynamicMetaProvider 内存实现
pub(all) struct InMemoryMetaProvider {
tables : Map[String, TableMeta]
}
///|
pub fn InMemoryMetaProvider::new() -> InMemoryMetaProvider {
{ tables: Map([]) }
}
///|
pub fn InMemoryMetaProvider::register(self : InMemoryMetaProvider, meta : TableMeta) -> Unit {
self.tables[meta.table_name] = meta
}
///|
pub fn InMemoryMetaProvider::get_table_meta(
self : InMemoryMetaProvider,
table_name : String
) -> TableMeta? {
self.tables.get(table_name)
}
///|
pub fn InMemoryMetaProvider::list_table_names(self : InMemoryMetaProvider) -> Array[String] {
let out : Array[String] = []
for k, _ in self.tables {
out.push(k)
}
out
}
///|
let reserved_prefixes : Array[String] = [
"sys_", "wf_", "mysql.", "information_schema.", "pg_", "sqlite_",
]
///|
/// 动态表名安全检查:仅字母数字下划线 + 保留前缀拒绝
pub fn is_table_name_safe(name : String) -> Bool {
if name.is_empty() {
return false
}
for ch in name {
let c = ch.to_int()
let alnum = (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122)
if !(alnum || ch == '_') {
return false
}
}
let lower = name.to_lower()
for prefix in reserved_prefixes {
if lower.has_prefix(prefix) {
return false
}
}
true
}
///|
/// 持久化模式
pub enum PersistMode {
Archive
Sync
} derive(Eq, Debug)
///|
pub fn PersistMode::from_str(s : String) -> PersistMode? {
match s.to_upper() {
"ARCHIVE" => Some(Archive)
"SYNC" => Some(Sync)
_ => None
}
}
///|
/// DynamicTableWriter trait(MoonBit:大接口 trait 形态)
pub(open) trait DynamicTableWriter {
fn insert(Self, String, Map[String, Json]) -> Int64 raise @error.JeeflowError
fn update(Self, String, Int64, Map[String, Json]) -> Unit raise @error.JeeflowError
fn exists(Self, String, Int64) -> Bool raise @error.JeeflowError
fn exists_by_key(Self, String, String, Int64) -> Bool raise @error.JeeflowError
fn update_by_key(Self, String, Map[String, Json], String, Int64) -> Unit raise @error.JeeflowError
// 探测实际存在的列(状态字段 {节点ID}_{状态码}/{节点ID} 过滤);缺省全保留
fn columns(Self, String, Array[String]) -> Array[String] raise @error.JeeflowError
}
///|
/// 系统字段填充(时间走 Clock 注入口,D-M0-3)
pub fn fill_system_fields(
data : Map[String, Json],
operator : String,
is_insert : Bool
) -> Unit {
let now = @model.current_time_str()
if is_insert {
data["create_time"] = @json.string_of(now)
data["create_user"] = @json.string_of(operator)
}
data["update_time"] = @json.string_of(now)
data["update_user"] = @json.string_of(operator)
}
// ── 显式 extend(implicit_impl_as_method 弃办:impl 的方法不再被隐式提升为常规方法)──
// 由 scripts/moon_warn_w2.py 从编译器消息里生成;一行一个 (类型, trait),可 diff 可复审。
pub extend PersistMode with Eq::{equal, not_equal}
pub extend PersistMode with Debug::{to_repr}
///|
/// IDynamicMetaProvider 的 MoonBit 同位(issues/146 缺口二)。
/// java 侧这个端口就定义在 jeeflow-persist/meta(不在 core),消费点是 MetaTableWriter/Reader
/// ⇒ 本栈同位同样落在 persist:归档/SYNC 的写侧与回显读侧共用一份元数据(storageType 语义两侧一致)。
/// load_table_meta 是 async:集成方的元数据来源是库(mldong dev_schema / information_schema),
/// 同步形状就会重演缺口一——启动期快照、改表结构引擎看不见。
/// 未注入 ⇒ 引擎行为与既往一致(InMemory 档照常,缺省不启用外部元数据)。
pub(all) struct DynamicMetaProviderFns {
load_table_meta : (async (String) -> TableMeta? raise @error.JeeflowError)
}
///|
/// 内存档 → 端口(纯内存读 ⇒ 经 @spi.async_of_sync 桥,注册点零警告)
pub fn InMemoryMetaProvider::as_provider(self : InMemoryMetaProvider) -> DynamicMetaProviderFns {
let it = self
let f : (String) -> TableMeta? raise @error.JeeflowError = name => it.get_table_meta(name)
{ load_table_meta: @spi.async_of_sync(f) }
}