logiguard fork v3: full patch set on verified 8c74db0 tree

Includes prior-session patches (carry forward so the app compiles):
  - crates/gpui/build.rs: cross-compile manifest fix
  - crates/gpui/src/platform.rs: PlatformWindow::activate_with_token trait method
  - crates/gpui/src/window.rs: Window::activate_with_token public API
  - crates/gpui_linux/src/linux/wayland/window.rs: WaylandWindow::activate_with_token + activate() keyboard-serial fix

Plus the focus-serial fix:
  - serial.rs: SerialKind::KeyboardEnter
  - client.rs: store wl_keyboard.enter serial; latest_serial_of()

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Mohamad Khani
2026-07-14 01:52:12 +03:30
commit b9819977a5
3984 changed files with 1487015 additions and 0 deletions

34
crates/db/Cargo.toml Normal file
View File

@@ -0,0 +1,34 @@
[package]
name = "db"
version = "0.1.0"
edition.workspace = true
publish.workspace = true
license = "GPL-3.0-or-later"
[lints]
workspace = true
[lib]
path = "src/db.rs"
doctest = false
[features]
test-support = []
[dependencies]
anyhow.workspace = true
gpui.workspace = true
indoc.workspace = true
inventory.workspace = true
log.workspace = true
paths.workspace = true
release_channel.workspace = true
sqlez.workspace = true
sqlez_macros.workspace = true
util.workspace = true
uuid.workspace = true
zed_env_vars.workspace = true
[dev-dependencies]
gpui = { workspace = true, features = ["test-support"] }
tempfile.workspace = true

1
crates/db/LICENSE-GPL Symbolic link
View File

@@ -0,0 +1 @@
../../LICENSE-GPL

5
crates/db/README.md Normal file
View File

@@ -0,0 +1,5 @@
# Building Queries
First, craft your test data. The examples folder shows a template for building a test-db, and can be ran with `cargo run --example [your-example]`.
To actually use and test your queries, import the generated DB file into https://sqliteonline.com/

417
crates/db/src/db.rs Normal file
View File

@@ -0,0 +1,417 @@
pub mod kvp;
pub mod query;
// Re-export
pub use anyhow;
use anyhow::Context as _;
pub use gpui;
use gpui::{App, AppContext, Global};
pub use indoc::indoc;
pub use inventory;
pub use paths::database_dir;
pub use sqlez;
pub use sqlez_macros;
pub use uuid;
pub use release_channel::RELEASE_CHANNEL;
use release_channel::ReleaseChannel;
use sqlez::domain::Migrator;
use sqlez::thread_safe_connection::ThreadSafeConnection;
use sqlez_macros::sql;
use std::fs::create_dir_all;
use std::future::Future;
use std::path::{Path, PathBuf};
use std::sync::atomic::AtomicBool;
use std::sync::{LazyLock, atomic::Ordering};
use util::{ResultExt, maybe};
use zed_env_vars::ZED_STATELESS;
/// A migration registered via `static_connection!` and collected at link time.
pub struct DomainMigration {
pub name: &'static str,
pub migrations: &'static [&'static str],
pub dependencies: &'static [&'static str],
pub should_allow_migration_change: fn(usize, &str, &str) -> bool,
}
inventory::collect!(DomainMigration);
/// The shared database connection backing all domain-specific DB wrappers.
/// Set as a GPUI global per-App. Falls back to a shared LazyLock if not set.
pub struct AppDatabase(pub ThreadSafeConnection);
impl Global for AppDatabase {}
/// Migrator that runs all inventory-registered domain migrations.
pub struct AppMigrator;
impl Migrator for AppMigrator {
fn migrate(connection: &sqlez::connection::Connection) -> anyhow::Result<()> {
let registrations: Vec<&DomainMigration> = inventory::iter::<DomainMigration>().collect();
let sorted = topological_sort(&registrations);
for reg in &sorted {
let mut should_allow = reg.should_allow_migration_change;
connection.migrate(reg.name, reg.migrations, &mut should_allow)?;
}
Ok(())
}
}
impl AppDatabase {
/// Opens the production database and runs all inventory-registered
/// migrations in dependency order.
pub fn new() -> Self {
let db_dir = database_dir();
let connection = gpui::block_on(open_db::<AppMigrator>(db_dir, *RELEASE_CHANNEL));
Self(connection)
}
/// Creates a new in-memory database with a unique name and runs all
/// inventory-registered migrations in dependency order.
#[cfg(any(test, feature = "test-support"))]
pub fn test_new() -> Self {
let name = format!("test-db-{}", uuid::Uuid::new_v4());
let connection = gpui::block_on(open_test_db::<AppMigrator>(&name));
Self(connection)
}
/// Returns the per-App connection if set, otherwise falls back to
/// the shared LazyLock.
pub fn global(cx: &App) -> &ThreadSafeConnection {
#[allow(unreachable_code)]
if let Some(db) = cx.try_global::<Self>() {
return &db.0;
} else {
#[cfg(any(feature = "test-support", test))]
return &TEST_APP_DATABASE.0;
panic!("database not initialized")
}
}
}
fn topological_sort<'a>(registrations: &[&'a DomainMigration]) -> Vec<&'a DomainMigration> {
let mut sorted: Vec<&DomainMigration> = Vec::new();
let mut visited: std::collections::HashSet<&str> = std::collections::HashSet::new();
fn visit<'a>(
name: &str,
registrations: &[&'a DomainMigration],
sorted: &mut Vec<&'a DomainMigration>,
visited: &mut std::collections::HashSet<&'a str>,
) {
if visited.contains(name) {
return;
}
if let Some(reg) = registrations.iter().find(|r| r.name == name) {
for dep in reg.dependencies {
visit(dep, registrations, sorted, visited);
}
visited.insert(reg.name);
sorted.push(reg);
}
}
for reg in registrations {
visit(reg.name, registrations, &mut sorted, &mut visited);
}
sorted
}
/// Shared fallback `AppDatabase` used when no per-App global is set.
#[cfg(any(test, feature = "test-support"))]
static TEST_APP_DATABASE: LazyLock<AppDatabase> = LazyLock::new(AppDatabase::test_new);
const CONNECTION_INITIALIZE_QUERY: &str = sql!(
PRAGMA foreign_keys=TRUE;
);
const DB_INITIALIZE_QUERY: &str = sql!(
PRAGMA journal_mode=WAL;
PRAGMA busy_timeout=500;
PRAGMA case_sensitive_like=TRUE;
PRAGMA synchronous=NORMAL;
);
const FALLBACK_DB_NAME: &str = "FALLBACK_MEMORY_DB";
const DB_FILE_NAME: &str = "db.sqlite";
pub static ALL_FILE_DB_FAILED: LazyLock<AtomicBool> = LazyLock::new(|| AtomicBool::new(false));
/// A type that can be used as a database scope for path construction.
pub trait DbScope {
fn scope_name(&self) -> &str;
}
impl DbScope for ReleaseChannel {
fn scope_name(&self) -> &str {
self.dev_name()
}
}
/// A database scope shared across all release channels.
pub struct GlobalDbScope;
impl DbScope for GlobalDbScope {
fn scope_name(&self) -> &str {
"global"
}
}
/// Returns the path to the `AppDatabase` SQLite file for the given scope
/// under `db_dir`.
pub fn db_path(db_dir: &Path, scope: impl DbScope) -> PathBuf {
db_dir
.join(format!("0-{}", scope.scope_name()))
.join(DB_FILE_NAME)
}
/// Open or create a database at the given directory path.
/// This will retry a couple times if there are failures. If opening fails once, the db directory
/// is moved to a backup folder and a new one is created. If that fails, a shared in memory db is created.
/// In either case, static variables are set so that the user can be notified.
pub async fn open_db<M: Migrator + 'static>(
db_dir: &Path,
scope: impl DbScope,
) -> ThreadSafeConnection {
if *ZED_STATELESS {
return open_fallback_db::<M>().await;
}
let db_path = db_path(db_dir, scope);
let connection = maybe!(async {
if let Some(parent) = db_path.parent() {
create_dir_all(parent)
.context("Could not create db directory")
.log_err()?;
}
open_main_db::<M>(&db_path).await
})
.await;
if let Some(connection) = connection {
return connection;
}
// Set another static ref so that we can escalate the notification
ALL_FILE_DB_FAILED.store(true, Ordering::Release);
// If still failed, create an in memory db with a known name
open_fallback_db::<M>().await
}
async fn open_main_db<M: Migrator>(db_path: &Path) -> Option<ThreadSafeConnection> {
log::trace!("Opening database {}", db_path.display());
ThreadSafeConnection::builder::<M>(db_path.to_string_lossy().as_ref(), true)
.with_db_initialization_query(DB_INITIALIZE_QUERY)
.with_connection_initialize_query(CONNECTION_INITIALIZE_QUERY)
.build()
.await
.log_err()
}
async fn open_fallback_db<M: Migrator>() -> ThreadSafeConnection {
log::warn!("Opening fallback in-memory database");
ThreadSafeConnection::builder::<M>(FALLBACK_DB_NAME, false)
.with_db_initialization_query(DB_INITIALIZE_QUERY)
.with_connection_initialize_query(CONNECTION_INITIALIZE_QUERY)
.build()
.await
.expect(
"Fallback in memory database failed. Likely initialization queries or migrations have fundamental errors",
)
}
#[cfg(any(test, feature = "test-support"))]
pub async fn open_test_db<M: Migrator>(db_name: &str) -> ThreadSafeConnection {
use sqlez::thread_safe_connection::locking_queue;
ThreadSafeConnection::builder::<M>(db_name, false)
.with_db_initialization_query(DB_INITIALIZE_QUERY)
.with_connection_initialize_query(CONNECTION_INITIALIZE_QUERY)
// Serialize queued writes via a mutex and run them synchronously
.with_write_queue_constructor(locking_queue())
.build()
.await
.unwrap()
}
/// Implements a basic DB wrapper for a given domain
///
/// Arguments:
/// - type of connection wrapper
/// - dependencies, whose migrations should be run prior to this domain's migrations
#[macro_export]
macro_rules! static_connection {
($t:ident, [ $($d:ty),* ]) => {
impl ::std::ops::Deref for $t {
type Target = $crate::sqlez::thread_safe_connection::ThreadSafeConnection;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ::std::clone::Clone for $t {
fn clone(&self) -> Self {
$t(self.0.clone())
}
}
impl $t {
/// Returns an instance backed by the per-App database if set,
/// or the shared fallback connection otherwise.
pub fn global(cx: &$crate::gpui::App) -> Self {
$t($crate::AppDatabase::global(cx).clone())
}
#[cfg(any(test, feature = "test-support"))]
pub async fn open_test_db(name: &'static str) -> Self {
$t($crate::open_test_db::<$t>(name).await)
}
}
$crate::inventory::submit! {
$crate::DomainMigration {
name: <$t as $crate::sqlez::domain::Domain>::NAME,
migrations: <$t as $crate::sqlez::domain::Domain>::MIGRATIONS,
dependencies: &[$(<$d as $crate::sqlez::domain::Domain>::NAME),*],
should_allow_migration_change: <$t as $crate::sqlez::domain::Domain>::should_allow_migration_change,
}
}
}
}
pub fn write_and_log<F>(cx: &App, db_write: impl FnOnce() -> F + Send + 'static)
where
F: Future<Output = anyhow::Result<()>> + Send,
{
cx.background_spawn(async move { db_write().await.log_err() })
.detach()
}
#[cfg(test)]
mod tests {
use std::thread;
use sqlez::domain::Domain;
use sqlez_macros::sql;
use crate::open_db;
// Test bad migration panics
#[gpui::test]
#[should_panic]
async fn test_bad_migration_panics() {
enum BadDB {}
impl Domain for BadDB {
const NAME: &str = "db_tests";
const MIGRATIONS: &[&str] = &[
sql!(CREATE TABLE test(value);),
// failure because test already exists
sql!(CREATE TABLE test(value);),
];
}
let tempdir = tempfile::Builder::new()
.prefix("DbTests")
.tempdir()
.unwrap();
let _bad_db = open_db::<BadDB>(tempdir.path(), release_channel::ReleaseChannel::Dev).await;
}
/// Test that DB exists but corrupted (causing recreate)
#[gpui::test]
async fn test_db_corruption(cx: &mut gpui::TestAppContext) {
cx.executor().allow_parking();
enum CorruptedDB {}
impl Domain for CorruptedDB {
const NAME: &str = "db_tests";
const MIGRATIONS: &[&str] = &[sql!(CREATE TABLE test(value);)];
}
enum GoodDB {}
impl Domain for GoodDB {
const NAME: &str = "db_tests"; //Notice same name
const MIGRATIONS: &[&str] = &[sql!(CREATE TABLE test2(value);)];
}
let tempdir = tempfile::Builder::new()
.prefix("DbTests")
.tempdir()
.unwrap();
{
let corrupt_db =
open_db::<CorruptedDB>(tempdir.path(), release_channel::ReleaseChannel::Dev).await;
assert!(corrupt_db.persistent());
}
let good_db = open_db::<GoodDB>(tempdir.path(), release_channel::ReleaseChannel::Dev).await;
assert!(
good_db.select_row::<usize>("SELECT * FROM test2").unwrap()()
.unwrap()
.is_none()
);
}
/// Test that DB exists but corrupted (causing recreate)
#[gpui::test(iterations = 30)]
async fn test_simultaneous_db_corruption(cx: &mut gpui::TestAppContext) {
cx.executor().allow_parking();
enum CorruptedDB {}
impl Domain for CorruptedDB {
const NAME: &str = "db_tests";
const MIGRATIONS: &[&str] = &[sql!(CREATE TABLE test(value);)];
}
enum GoodDB {}
impl Domain for GoodDB {
const NAME: &str = "db_tests"; //Notice same name
const MIGRATIONS: &[&str] = &[sql!(CREATE TABLE test2(value);)]; // But different migration
}
let tempdir = tempfile::Builder::new()
.prefix("DbTests")
.tempdir()
.unwrap();
{
// Setup the bad database
let corrupt_db =
open_db::<CorruptedDB>(tempdir.path(), release_channel::ReleaseChannel::Dev).await;
assert!(corrupt_db.persistent());
}
// Try to connect to it a bunch of times at once
let mut guards = vec![];
for _ in 0..10 {
let tmp_path = tempdir.path().to_path_buf();
let guard = thread::spawn(move || {
let good_db = gpui::block_on(open_db::<GoodDB>(
tmp_path.as_path(),
release_channel::ReleaseChannel::Dev,
));
assert!(
good_db.select_row::<usize>("SELECT * FROM test2").unwrap()()
.unwrap()
.is_none()
);
});
guards.push(guard);
}
for guard in guards.into_iter() {
assert!(guard.join().is_ok());
}
}
}

279
crates/db/src/kvp.rs Normal file
View File

@@ -0,0 +1,279 @@
use anyhow::Context as _;
use gpui::App;
use sqlez_macros::sql;
use util::ResultExt as _;
use crate::{
query,
sqlez::{domain::Domain, thread_safe_connection::ThreadSafeConnection},
write_and_log,
};
pub struct KeyValueStore(crate::sqlez::thread_safe_connection::ThreadSafeConnection);
impl KeyValueStore {
pub fn from_app_db(db: &crate::AppDatabase) -> Self {
Self(db.0.clone())
}
}
impl Domain for KeyValueStore {
const NAME: &str = stringify!(KeyValueStore);
const MIGRATIONS: &[&str] = &[
sql!(
CREATE TABLE IF NOT EXISTS kv_store(
key TEXT PRIMARY KEY,
value TEXT NOT NULL
) STRICT;
),
sql!(
CREATE TABLE IF NOT EXISTS scoped_kv_store(
namespace TEXT NOT NULL,
key TEXT NOT NULL,
value TEXT NOT NULL,
PRIMARY KEY(namespace, key)
) STRICT;
),
];
}
crate::static_connection!(KeyValueStore, []);
pub trait Dismissable {
const KEY: &'static str;
fn dismissed(cx: &App) -> bool {
KeyValueStore::global(cx)
.read_kvp(Self::KEY)
.log_err()
.is_some_and(|s| s.is_some())
}
fn set_dismissed(is_dismissed: bool, cx: &mut App) {
let db = KeyValueStore::global(cx);
write_and_log(cx, move || async move {
if is_dismissed {
db.write_kvp(Self::KEY.into(), "1".into()).await
} else {
db.delete_kvp(Self::KEY.into()).await
}
})
}
}
impl KeyValueStore {
query! {
pub fn read_kvp(key: &str) -> Result<Option<String>> {
SELECT value FROM kv_store WHERE key = (?)
}
}
pub async fn write_kvp(&self, key: String, value: String) -> anyhow::Result<()> {
log::debug!("Writing key-value pair for key {key}");
self.write_kvp_inner(key, value).await
}
query! {
async fn write_kvp_inner(key: String, value: String) -> Result<()> {
INSERT OR REPLACE INTO kv_store(key, value) VALUES ((?), (?))
}
}
query! {
pub async fn delete_kvp(key: String) -> Result<()> {
DELETE FROM kv_store WHERE key = (?)
}
}
pub fn scoped<'a>(&'a self, namespace: &'a str) -> ScopedKeyValueStore<'a> {
ScopedKeyValueStore {
store: self,
namespace,
}
}
}
pub struct ScopedKeyValueStore<'a> {
store: &'a KeyValueStore,
namespace: &'a str,
}
impl ScopedKeyValueStore<'_> {
pub fn read(&self, key: &str) -> anyhow::Result<Option<String>> {
self.store.select_row_bound::<(&str, &str), String>(
"SELECT value FROM scoped_kv_store WHERE namespace = (?) AND key = (?)",
)?((self.namespace, key))
.context("Failed to read from scoped_kv_store")
}
pub async fn write(&self, key: String, value: String) -> anyhow::Result<()> {
let namespace = self.namespace.to_owned();
self.store
.write(move |connection| {
connection.exec_bound::<(&str, &str, &str)>(
"INSERT OR REPLACE INTO scoped_kv_store(namespace, key, value) VALUES ((?), (?), (?))",
)?((&namespace, &key, &value))
.context("Failed to write to scoped_kv_store")
})
.await
}
pub async fn delete(&self, key: String) -> anyhow::Result<()> {
let namespace = self.namespace.to_owned();
self.store
.write(move |connection| {
connection.exec_bound::<(&str, &str)>(
"DELETE FROM scoped_kv_store WHERE namespace = (?) AND key = (?)",
)?((&namespace, &key))
.context("Failed to delete from scoped_kv_store")
})
.await
}
pub async fn delete_all(&self) -> anyhow::Result<()> {
let namespace = self.namespace.to_owned();
self.store
.write(move |connection| {
connection
.exec_bound::<&str>("DELETE FROM scoped_kv_store WHERE namespace = (?)")?(
&namespace,
)
.context("Failed to delete_all from scoped_kv_store")
})
.await
}
}
#[cfg(test)]
mod tests {
use crate::kvp::KeyValueStore;
#[gpui::test]
async fn test_kvp() {
let db = KeyValueStore::open_test_db("test_kvp").await;
assert_eq!(db.read_kvp("key-1").unwrap(), None);
db.write_kvp("key-1".to_string(), "one".to_string())
.await
.unwrap();
assert_eq!(db.read_kvp("key-1").unwrap(), Some("one".to_string()));
db.write_kvp("key-1".to_string(), "one-2".to_string())
.await
.unwrap();
assert_eq!(db.read_kvp("key-1").unwrap(), Some("one-2".to_string()));
db.write_kvp("key-2".to_string(), "two".to_string())
.await
.unwrap();
assert_eq!(db.read_kvp("key-2").unwrap(), Some("two".to_string()));
db.delete_kvp("key-1".to_string()).await.unwrap();
assert_eq!(db.read_kvp("key-1").unwrap(), None);
}
#[gpui::test]
async fn test_scoped_kvp() {
let db = KeyValueStore::open_test_db("test_scoped_kvp").await;
let scope_a = db.scoped("namespace-a");
let scope_b = db.scoped("namespace-b");
// Reading a missing key returns None
assert_eq!(scope_a.read("key-1").unwrap(), None);
// Writing and reading back a key works
scope_a
.write("key-1".to_string(), "value-a1".to_string())
.await
.unwrap();
assert_eq!(scope_a.read("key-1").unwrap(), Some("value-a1".to_string()));
// Two namespaces with the same key don't collide
scope_b
.write("key-1".to_string(), "value-b1".to_string())
.await
.unwrap();
assert_eq!(scope_a.read("key-1").unwrap(), Some("value-a1".to_string()));
assert_eq!(scope_b.read("key-1").unwrap(), Some("value-b1".to_string()));
// delete removes a single key without affecting others in the namespace
scope_a
.write("key-2".to_string(), "value-a2".to_string())
.await
.unwrap();
scope_a.delete("key-1".to_string()).await.unwrap();
assert_eq!(scope_a.read("key-1").unwrap(), None);
assert_eq!(scope_a.read("key-2").unwrap(), Some("value-a2".to_string()));
assert_eq!(scope_b.read("key-1").unwrap(), Some("value-b1".to_string()));
// delete_all removes all keys in a namespace without affecting other namespaces
scope_a
.write("key-3".to_string(), "value-a3".to_string())
.await
.unwrap();
scope_a.delete_all().await.unwrap();
assert_eq!(scope_a.read("key-2").unwrap(), None);
assert_eq!(scope_a.read("key-3").unwrap(), None);
assert_eq!(scope_b.read("key-1").unwrap(), Some("value-b1".to_string()));
}
}
pub struct GlobalKeyValueStore(ThreadSafeConnection);
impl Domain for GlobalKeyValueStore {
const NAME: &str = stringify!(GlobalKeyValueStore);
const MIGRATIONS: &[&str] = &[sql!(
CREATE TABLE IF NOT EXISTS kv_store(
key TEXT PRIMARY KEY,
value TEXT NOT NULL
) STRICT;
)];
}
impl std::ops::Deref for GlobalKeyValueStore {
type Target = ThreadSafeConnection;
fn deref(&self) -> &Self::Target {
&self.0
}
}
static GLOBAL_KEY_VALUE_STORE: std::sync::LazyLock<GlobalKeyValueStore> =
std::sync::LazyLock::new(|| {
let db_dir = crate::database_dir();
GlobalKeyValueStore(gpui::block_on(crate::open_db::<GlobalKeyValueStore>(
db_dir,
crate::GlobalDbScope,
)))
});
impl GlobalKeyValueStore {
pub fn global() -> &'static Self {
&GLOBAL_KEY_VALUE_STORE
}
query! {
pub fn read_kvp(key: &str) -> Result<Option<String>> {
SELECT value FROM kv_store WHERE key = (?)
}
}
pub async fn write_kvp(&self, key: String, value: String) -> anyhow::Result<()> {
log::debug!("Writing global key-value pair for key {key}");
self.write_kvp_inner(key, value).await
}
query! {
async fn write_kvp_inner(key: String, value: String) -> Result<()> {
INSERT OR REPLACE INTO kv_store(key, value) VALUES ((?), (?))
}
}
query! {
pub async fn delete_kvp(key: String) -> Result<()> {
DELETE FROM kv_store WHERE key = (?)
}
}
}

314
crates/db/src/query.rs Normal file
View File

@@ -0,0 +1,314 @@
#[macro_export]
macro_rules! query {
($vis:vis fn $id:ident() -> Result<()> { $($sql:tt)+ }) => {
$vis fn $id(&self) -> $crate::anyhow::Result<()> {
use $crate::anyhow::Context;
let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
self.exec(sql_stmt)?().context(::std::format!(
"Error in {}, exec failed to execute or parse for: {}",
::std::stringify!($id),
sql_stmt,
))
}
};
($vis:vis async fn $id:ident() -> Result<()> { $($sql:tt)+ }) => {
$vis async fn $id(&self) -> $crate::anyhow::Result<()> {
use $crate::anyhow::Context;
self.write(|connection| {
let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
connection.exec(sql_stmt)?().context(::std::format!(
"Error in {}, exec failed to execute or parse for: {}",
::std::stringify!($id),
sql_stmt
))
}).await
}
};
($vis:vis fn $id:ident($($arg:ident: $arg_type:ty),+) -> Result<()> { $($sql:tt)+ }) => {
$vis fn $id(&self, $($arg: $arg_type),+) -> $crate::anyhow::Result<()> {
use $crate::anyhow::Context;
let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
self.exec_bound::<($($arg_type),+)>(sql_stmt)?(($($arg),+))
.context(::std::format!(
"Error in {}, exec_bound failed to execute or parse for: {}",
::std::stringify!($id),
sql_stmt
))
}
};
($vis:vis async fn $id:ident($arg:ident: $arg_type:ty) -> Result<()> { $($sql:tt)+ }) => {
$vis async fn $id(&self, $arg: $arg_type) -> $crate::anyhow::Result<()> {
use $crate::anyhow::Context;
self.write(move |connection| {
let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
connection.exec_bound::<$arg_type>(sql_stmt)?($arg)
.context(::std::format!(
"Error in {}, exec_bound failed to execute or parse for: {}",
::std::stringify!($id),
sql_stmt
))
}).await
}
};
($vis:vis async fn $id:ident($($arg:ident: $arg_type:ty),+) -> Result<()> { $($sql:tt)+ }) => {
$vis async fn $id(&self, $($arg: $arg_type),+) -> $crate::anyhow::Result<()> {
use $crate::anyhow::Context;
self.write(move |connection| {
let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
connection.exec_bound::<($($arg_type),+)>(sql_stmt)?(($($arg),+))
.context(::std::format!(
"Error in {}, exec_bound failed to execute or parse for: {}",
::std::stringify!($id),
sql_stmt
))
}).await
}
};
($vis:vis fn $id:ident() -> Result<Vec<$return_type:ty>> { $($sql:tt)+ }) => {
$vis fn $id(&self) -> $crate::anyhow::Result<Vec<$return_type>> {
use $crate::anyhow::Context;
let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
self.select::<$return_type>(sql_stmt)?()
.context(::std::format!(
"Error in {}, select_row failed to execute or parse for: {}",
::std::stringify!($id),
sql_stmt
))
}
};
($vis:vis async fn $id:ident() -> Result<Vec<$return_type:ty>> { $($sql:tt)+ }) => {
pub async fn $id(&self) -> $crate::anyhow::Result<Vec<$return_type>> {
use $crate::anyhow::Context;
self.write(|connection| {
let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
connection.select::<$return_type>(sql_stmt)?()
.context(::std::format!(
"Error in {}, select_row failed to execute or parse for: {}",
::std::stringify!($id),
sql_stmt
))
}).await
}
};
($vis:vis fn $id:ident($($arg:ident: $arg_type:ty),+) -> Result<Vec<$return_type:ty>> { $($sql:tt)+ }) => {
$vis fn $id(&self, $($arg: $arg_type),+) -> $crate::anyhow::Result<Vec<$return_type>> {
use $crate::anyhow::Context;
let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
self.select_bound::<($($arg_type),+), $return_type>(sql_stmt)?(($($arg),+))
.context(::std::format!(
"Error in {}, exec_bound failed to execute or parse for: {}",
::std::stringify!($id),
sql_stmt
))
}
};
($vis:vis async fn $id:ident($($arg:ident: $arg_type:ty),+) -> Result<Vec<$return_type:ty>> { $($sql:tt)+ }) => {
$vis async fn $id(&self, $($arg: $arg_type),+) -> $crate::anyhow::Result<Vec<$return_type>> {
use $crate::anyhow::Context;
self.write(|connection| {
let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
connection.select_bound::<($($arg_type),+), $return_type>(sql_stmt)?(($($arg),+))
.context(::std::format!(
"Error in {}, exec_bound failed to execute or parse for: {}",
::std::stringify!($id),
sql_stmt
))
}).await
}
};
($vis:vis fn $id:ident() -> Result<Option<$return_type:ty>> { $($sql:tt)+ }) => {
$vis fn $id(&self) -> $crate::anyhow::Result<Option<$return_type>> {
use $crate::anyhow::Context;
let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
self.select_row::<$return_type>(sql_stmt)?()
.context(::std::format!(
"Error in {}, select_row failed to execute or parse for: {}",
::std::stringify!($id),
sql_stmt
))
}
};
($vis:vis async fn $id:ident() -> Result<Option<$return_type:ty>> { $($sql:tt)+ }) => {
$vis async fn $id(&self) -> $crate::anyhow::Result<Option<$return_type>> {
use $crate::anyhow::Context;
self.write(|connection| {
let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
connection.select_row::<$return_type>(sql_stmt)?()
.context(::std::format!(
"Error in {}, select_row failed to execute or parse for: {}",
::std::stringify!($id),
sql_stmt
))
}).await
}
};
($vis:vis fn $id:ident($arg:ident: $arg_type:ty) -> Result<Option<$return_type:ty>> { $($sql:tt)+ }) => {
$vis fn $id(&self, $arg: $arg_type) -> $crate::anyhow::Result<Option<$return_type>> {
use $crate::anyhow::Context;
let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
self.select_row_bound::<$arg_type, $return_type>(sql_stmt)?($arg)
.context(::std::format!(
"Error in {}, select_row_bound failed to execute or parse for: {}",
::std::stringify!($id),
sql_stmt
))
}
};
($vis:vis fn $id:ident($($arg:ident: $arg_type:ty),+) -> Result<Option<$return_type:ty>> { $($sql:tt)+ }) => {
$vis fn $id(&self, $($arg: $arg_type),+) -> $crate::anyhow::Result<Option<$return_type>> {
use $crate::anyhow::Context;
let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
self.select_row_bound::<($($arg_type),+), $return_type>(sql_stmt)?(($($arg),+))
.context(::std::format!(
"Error in {}, select_row_bound failed to execute or parse for: {}",
::std::stringify!($id),
sql_stmt
))
}
};
($vis:vis async fn $id:ident($($arg:ident: $arg_type:ty),+) -> Result<Option<$return_type:ty>> { $($sql:tt)+ }) => {
$vis async fn $id(&self, $($arg: $arg_type),+) -> $crate::anyhow::Result<Option<$return_type>> {
use $crate::anyhow::Context;
self.write(move |connection| {
let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
connection.select_row_bound::<($($arg_type),+), $return_type>(sql_stmt)?(($($arg),+))
.context(::std::format!(
"Error in {}, select_row_bound failed to execute or parse for: {}",
::std::stringify!($id),
sql_stmt
))
}).await
}
};
($vis:vis fn $id:ident() -> Result<$return_type:ty> { $($sql:tt)+ }) => {
$vis fn $id(&self) -> $crate::anyhow::Result<$return_type> {
use $crate::anyhow::Context;
let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
self.select_row::<$return_type>(sql_stmt)?()
.context(::std::format!(
"Error in {}, select_row_bound failed to execute or parse for: {}",
::std::stringify!($id),
sql_stmt
))?
.context(::std::format!(
"Error in {}, select_row_bound expected single row result but found none for: {}",
::std::stringify!($id),
sql_stmt
))
}
};
($vis:vis async fn $id:ident() -> Result<$return_type:ty> { $($sql:tt)+ }) => {
$vis async fn $id(&self) -> $crate::anyhow::Result<$return_type> {
use $crate::anyhow::Context;
self.write(|connection| {
let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
connection.select_row::<$return_type>(sql_stmt)?()
.context(::std::format!(
"Error in {}, select_row_bound failed to execute or parse for: {}",
::std::stringify!($id),
sql_stmt
))?
.context(::std::format!(
"Error in {}, select_row_bound expected single row result but found none for: {}",
::std::stringify!($id),
sql_stmt
))
}).await
}
};
($vis:vis fn $id:ident($arg:ident: $arg_type:ty) -> Result<$return_type:ty> { $($sql:tt)+ }) => {
pub fn $id(&self, $arg: $arg_type) -> $crate::anyhow::Result<$return_type> {
use $crate::anyhow::Context;
let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
self.select_row_bound::<$arg_type, $return_type>(sql_stmt)?($arg)
.context(::std::format!(
"Error in {}, select_row_bound failed to execute or parse for: {}",
::std::stringify!($id),
sql_stmt
))?
.context(::std::format!(
"Error in {}, select_row_bound expected single row result but found none for: {}",
::std::stringify!($id),
sql_stmt
))
}
};
($vis:vis fn $id:ident($($arg:ident: $arg_type:ty),+) -> Result<$return_type:ty> { $($sql:tt)+ }) => {
$vis fn $id(&self, $($arg: $arg_type),+) -> $crate::anyhow::Result<$return_type> {
use $crate::anyhow::Context;
let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
self.select_row_bound::<($($arg_type),+), $return_type>(sql_stmt)?(($($arg),+))
.context(::std::format!(
"Error in {}, select_row_bound failed to execute or parse for: {}",
::std::stringify!($id),
sql_stmt
))?
.context(::std::format!(
"Error in {}, select_row_bound expected single row result but found none for: {}",
::std::stringify!($id),
sql_stmt
))
}
};
($vis:vis fn async $id:ident($($arg:ident: $arg_type:ty),+) -> Result<$return_type:ty> { $($sql:tt)+ }) => {
$vis async fn $id(&self, $($arg: $arg_type),+) -> $crate::anyhow::Result<$return_type> {
use $crate::anyhow::Context;
self.write(|connection| {
let sql_stmt = $crate::sqlez_macros::sql!($($sql)+);
connection.select_row_bound::<($($arg_type),+), $return_type>(sql_stmt)?(($($arg),+))
.context(::std::format!(
"Error in {}, select_row_bound failed to execute or parse for: {}",
::std::stringify!($id),
sql_stmt
))?
.context(::std::format!(
"Error in {}, select_row_bound expected single row result but found none for: {}",
::std::stringify!($id),
sql_stmt
))
}).await
}
};
}