pub mod atomic_write;
pub mod availability;
pub mod canvas;
pub mod command;
pub mod core;
mod database;
pub mod event;
pub use datacraft_expr as expr;
pub mod failure;
pub mod field_expressions;
pub mod form;
mod forms;
pub mod grid;
pub mod ids;
pub mod import;
pub mod journal;
pub mod keys;
pub mod macro_def;
pub use datacraft_query::native_exec;
pub mod nav;
pub mod palette;
pub mod port;
mod port_mock;
pub mod present;
pub use datacraft_query::qbe;
mod qbe_dispatch;
pub use datacraft_query::query_criteria;
pub use datacraft_query::query_engine;
pub mod report;
pub mod report_aggregates;
pub mod report_bands;
pub mod report_preview;
mod reports;
pub mod ribbon;
pub mod row_edit;
pub mod schema;
pub mod snapshot;
#[cfg(feature = "sql-engine")]
pub use datacraft_query::sql_engine;
pub use datacraft_query::sql_types;
pub mod state;
pub mod table_view;
pub mod templates;
pub mod transport;
pub mod undo;
pub mod vba;
pub mod vba_analysis;
mod vba_dispatch;
pub mod view;
pub use ids::TabId;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ObjectKind {
Table,
Query,
View,
Form,
Report,
Macro,
Module,
}
impl ObjectKind {
pub fn slug(self) -> &'static str {
match self {
ObjectKind::Table => "table",
ObjectKind::Query => "query",
ObjectKind::View => "view",
ObjectKind::Form => "form",
ObjectKind::Report => "report",
ObjectKind::Macro => "macro",
ObjectKind::Module => "module",
}
}
pub fn from_slug(slug: &str) -> Option<Self> {
Some(match slug {
"table" => Self::Table,
"query" => Self::Query,
"view" => Self::View,
"form" => Self::Form,
"report" => Self::Report,
"macro" => Self::Macro,
"module" => Self::Module,
_ => return None,
})
}
pub fn has_design_view(self) -> bool {
matches!(
self,
ObjectKind::Table | ObjectKind::Query | ObjectKind::Form | ObjectKind::Report
)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ObjectId {
pub kind: ObjectKind,
pub name: String,
}
impl ObjectId {
pub fn new(kind: ObjectKind, name: impl Into<String>) -> Self {
Self {
kind,
name: name.into(),
}
}
pub fn ui_test_id(&self) -> String {
format!("node.{}.{}", self.kind.slug(), self.name.to_lowercase())
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
#[test]
fn object_id_renders_deterministic_node_test_id() {
let id = ObjectId::new(ObjectKind::Table, "Customers");
assert_eq!(id.ui_test_id(), "node.table.customers");
}
#[test]
fn object_kind_slugs_are_lower_case_names() {
assert_eq!(ObjectKind::Table.slug(), "table");
assert_eq!(ObjectKind::Query.slug(), "query");
assert_eq!(ObjectKind::View.slug(), "view");
assert_eq!(ObjectKind::Form.slug(), "form");
assert_eq!(ObjectKind::Report.slug(), "report");
assert_eq!(ObjectKind::Macro.slug(), "macro");
assert_eq!(ObjectKind::Module.slug(), "module");
for kind in [
ObjectKind::Table,
ObjectKind::Query,
ObjectKind::View,
ObjectKind::Form,
ObjectKind::Report,
ObjectKind::Macro,
ObjectKind::Module,
] {
assert_eq!(ObjectKind::from_slug(kind.slug()), Some(kind));
}
assert_eq!(ObjectKind::from_slug("entity"), None);
}
proptest! {
#[test]
fn object_id_json_roundtrip_preserves_identity(name in ".*") {
let id = ObjectId::new(ObjectKind::Query, name.as_str());
let json = serde_json::to_string(&id).expect("serialize ObjectId");
let back: ObjectId = serde_json::from_str(&json).expect("deserialize ObjectId");
prop_assert_eq!(back, id);
}
}
}