Checkpoint

Antonio Scandurra created

Change summary

Cargo.lock              |  23 +++
Cargo.toml              |   5 
crates/db2/Cargo.toml   |  33 ++++
crates/db2/README.md    |   5 
crates/db2/src/db2.rs   | 327 +++++++++++++++++++++++++++++++++++++++++++
crates/db2/src/kvp.rs   |  62 ++++++++
crates/db2/src/query.rs | 314 +++++++++++++++++++++++++++++++++++++++++
crates/zed2/Cargo.toml  |   2 
crates/zed2/src/main.rs |  39 ++--
9 files changed, 789 insertions(+), 21 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -2209,6 +2209,28 @@ dependencies = [
  "util",
 ]
 
+[[package]]
+name = "db2"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "collections",
+ "env_logger 0.9.3",
+ "gpui2",
+ "indoc",
+ "lazy_static",
+ "log",
+ "parking_lot 0.11.2",
+ "serde",
+ "serde_derive",
+ "smol",
+ "sqlez",
+ "sqlez_macros",
+ "tempdir",
+ "util",
+]
+
 [[package]]
 name = "deflate"
 version = "0.8.6"
@@ -10427,6 +10449,7 @@ dependencies = [
  "client2",
  "collections",
  "ctor",
+ "db2",
  "env_logger 0.9.3",
  "feature_flags",
  "fs",

Cargo.toml 🔗

@@ -21,11 +21,14 @@ members = [
     "crates/copilot",
     "crates/copilot_button",
     "crates/db",
+    "crates/db2",
     "crates/refineable",
     "crates/refineable/derive_refineable",
     "crates/diagnostics",
     "crates/drag_and_drop",
     "crates/editor",
+    "crates/feature_flags",
+    "crates/feature_flags2",
     "crates/feedback",
     "crates/file_finder",
     "crates/fs",
@@ -62,10 +65,10 @@ members = [
     "crates/rpc",
     "crates/search",
     "crates/settings",
+    "crates/settings2",
     "crates/snippet",
     "crates/sqlez",
     "crates/sqlez_macros",
-    "crates/feature_flags",
     "crates/rich_text",
     "crates/storybook2",
     "crates/sum_tree",

crates/db2/Cargo.toml 🔗

@@ -0,0 +1,33 @@
+[package]
+name = "db2"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+path = "src/db2.rs"
+doctest = false
+
+[features]
+test-support = []
+
+[dependencies]
+collections = { path = "../collections" }
+gpui2 = { path = "../gpui2" }
+sqlez = { path = "../sqlez" }
+sqlez_macros = { path = "../sqlez_macros" }
+util = { path = "../util" }
+anyhow.workspace = true
+indoc.workspace = true
+async-trait.workspace = true
+lazy_static.workspace = true
+log.workspace = true
+parking_lot.workspace = true
+serde.workspace = true
+serde_derive.workspace = true
+smol.workspace = true
+
+[dev-dependencies]
+gpui2 = { path = "../gpui2", features = ["test-support"] }
+env_logger.workspace = true
+tempdir.workspace = true

crates/db2/README.md 🔗

@@ -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/

crates/db2/src/db2.rs 🔗

@@ -0,0 +1,327 @@
+pub mod kvp;
+pub mod query;
+
+// Re-export
+pub use anyhow;
+use anyhow::Context;
+use gpui2::AppContext;
+pub use indoc::indoc;
+pub use lazy_static;
+pub use smol;
+pub use sqlez;
+pub use sqlez_macros;
+pub use util::channel::{RELEASE_CHANNEL, RELEASE_CHANNEL_NAME};
+pub use util::paths::DB_DIR;
+
+use sqlez::domain::Migrator;
+use sqlez::thread_safe_connection::ThreadSafeConnection;
+use sqlez_macros::sql;
+use std::future::Future;
+use std::path::{Path, PathBuf};
+use std::sync::atomic::{AtomicBool, Ordering};
+use util::channel::ReleaseChannel;
+use util::{async_iife, ResultExt};
+
+const CONNECTION_INITIALIZE_QUERY: &'static str = sql!(
+    PRAGMA foreign_keys=TRUE;
+);
+
+const DB_INITIALIZE_QUERY: &'static str = sql!(
+    PRAGMA journal_mode=WAL;
+    PRAGMA busy_timeout=1;
+    PRAGMA case_sensitive_like=TRUE;
+    PRAGMA synchronous=NORMAL;
+);
+
+const FALLBACK_DB_NAME: &'static str = "FALLBACK_MEMORY_DB";
+
+const DB_FILE_NAME: &'static str = "db.sqlite";
+
+lazy_static::lazy_static! {
+    pub static ref ZED_STATELESS: bool = std::env::var("ZED_STATELESS").map_or(false, |v| !v.is_empty());
+    pub static ref ALL_FILE_DB_FAILED: AtomicBool = AtomicBool::new(false);
+}
+
+/// 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,
+    release_channel: &ReleaseChannel,
+) -> ThreadSafeConnection<M> {
+    if *ZED_STATELESS {
+        return open_fallback_db().await;
+    }
+
+    let release_channel_name = release_channel.dev_name();
+    let main_db_dir = db_dir.join(Path::new(&format!("0-{}", release_channel_name)));
+
+    let connection = async_iife!({
+        smol::fs::create_dir_all(&main_db_dir)
+            .await
+            .context("Could not create db directory")
+            .log_err()?;
+        let db_path = main_db_dir.join(Path::new(DB_FILE_NAME));
+        open_main_db(&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().await
+}
+
+async fn open_main_db<M: Migrator>(db_path: &PathBuf) -> Option<ThreadSafeConnection<M>> {
+    log::info!("Opening main db");
+    ThreadSafeConnection::<M>::builder(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<M> {
+    log::info!("Opening fallback db");
+    ThreadSafeConnection::<M>::builder(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<M> {
+    use sqlez::thread_safe_connection::locking_queue;
+
+    ThreadSafeConnection::<M>::builder(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
+#[macro_export]
+macro_rules! define_connection {
+    (pub static ref $id:ident: $t:ident<()> = $migrations:expr;) => {
+        pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection<$t>);
+
+        impl ::std::ops::Deref for $t {
+            type Target = $crate::sqlez::thread_safe_connection::ThreadSafeConnection<$t>;
+
+            fn deref(&self) -> &Self::Target {
+                &self.0
+            }
+        }
+
+        impl $crate::sqlez::domain::Domain for $t {
+            fn name() -> &'static str {
+                stringify!($t)
+            }
+
+            fn migrations() -> &'static [&'static str] {
+                $migrations
+            }
+        }
+
+        #[cfg(any(test, feature = "test-support"))]
+        $crate::lazy_static::lazy_static! {
+            pub static ref $id: $t = $t($crate::smol::block_on($crate::open_test_db(stringify!($id))));
+        }
+
+        #[cfg(not(any(test, feature = "test-support")))]
+        $crate::lazy_static::lazy_static! {
+            pub static ref $id: $t = $t($crate::smol::block_on($crate::open_db(&$crate::DB_DIR, &$crate::RELEASE_CHANNEL)));
+        }
+    };
+    (pub static ref $id:ident: $t:ident<$($d:ty),+> = $migrations:expr;) => {
+        pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection<( $($d),+, $t )>);
+
+        impl ::std::ops::Deref for $t {
+            type Target = $crate::sqlez::thread_safe_connection::ThreadSafeConnection<($($d),+, $t)>;
+
+            fn deref(&self) -> &Self::Target {
+                &self.0
+            }
+        }
+
+        impl $crate::sqlez::domain::Domain for $t {
+            fn name() -> &'static str {
+                stringify!($t)
+            }
+
+            fn migrations() -> &'static [&'static str] {
+                $migrations
+            }
+        }
+
+        #[cfg(any(test, feature = "test-support"))]
+        $crate::lazy_static::lazy_static! {
+            pub static ref $id: $t = $t($crate::smol::block_on($crate::open_test_db(stringify!($id))));
+        }
+
+        #[cfg(not(any(test, feature = "test-support")))]
+        $crate::lazy_static::lazy_static! {
+            pub static ref $id: $t = $t($crate::smol::block_on($crate::open_db(&$crate::DB_DIR, &$crate::RELEASE_CHANNEL)));
+        }
+    };
+}
+
+pub fn write_and_log<F>(cx: &mut AppContext, db_write: impl FnOnce() -> F + Send + 'static)
+where
+    F: Future<Output = anyhow::Result<()>> + Send,
+{
+    cx.executor()
+        .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 tempdir::TempDir;
+
+//     use crate::open_db;
+
+//     // Test bad migration panics
+//     #[gpui::test]
+//     #[should_panic]
+//     async fn test_bad_migration_panics() {
+//         enum BadDB {}
+
+//         impl Domain for BadDB {
+//             fn name() -> &'static str {
+//                 "db_tests"
+//             }
+
+//             fn migrations() -> &'static [&'static str] {
+//                 &[
+//                     sql!(CREATE TABLE test(value);),
+//                     // failure because test already exists
+//                     sql!(CREATE TABLE test(value);),
+//                 ]
+//             }
+//         }
+
+//         let tempdir = TempDir::new("DbTests").unwrap();
+//         let _bad_db = open_db::<BadDB>(tempdir.path(), &util::channel::ReleaseChannel::Dev).await;
+//     }
+
+//     /// Test that DB exists but corrupted (causing recreate)
+//     #[gpui::test]
+//     async fn test_db_corruption() {
+//         enum CorruptedDB {}
+
+//         impl Domain for CorruptedDB {
+//             fn name() -> &'static str {
+//                 "db_tests"
+//             }
+
+//             fn migrations() -> &'static [&'static str] {
+//                 &[sql!(CREATE TABLE test(value);)]
+//             }
+//         }
+
+//         enum GoodDB {}
+
+//         impl Domain for GoodDB {
+//             fn name() -> &'static str {
+//                 "db_tests" //Notice same name
+//             }
+
+//             fn migrations() -> &'static [&'static str] {
+//                 &[sql!(CREATE TABLE test2(value);)] //But different migration
+//             }
+//         }
+
+//         let tempdir = TempDir::new("DbTests").unwrap();
+//         {
+//             let corrupt_db =
+//                 open_db::<CorruptedDB>(tempdir.path(), &util::channel::ReleaseChannel::Dev).await;
+//             assert!(corrupt_db.persistent());
+//         }
+
+//         let good_db = open_db::<GoodDB>(tempdir.path(), &util::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() {
+//         enum CorruptedDB {}
+
+//         impl Domain for CorruptedDB {
+//             fn name() -> &'static str {
+//                 "db_tests"
+//             }
+
+//             fn migrations() -> &'static [&'static str] {
+//                 &[sql!(CREATE TABLE test(value);)]
+//             }
+//         }
+
+//         enum GoodDB {}
+
+//         impl Domain for GoodDB {
+//             fn name() -> &'static str {
+//                 "db_tests" //Notice same name
+//             }
+
+//             fn migrations() -> &'static [&'static str] {
+//                 &[sql!(CREATE TABLE test2(value);)] //But different migration
+//             }
+//         }
+
+//         let tempdir = TempDir::new("DbTests").unwrap();
+//         {
+//             // Setup the bad database
+//             let corrupt_db =
+//                 open_db::<CorruptedDB>(tempdir.path(), &util::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 = smol::block_on(open_db::<GoodDB>(
+//                     tmp_path.as_path(),
+//                     &util::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());
+//         }
+//     }
+// }

crates/db2/src/kvp.rs 🔗

@@ -0,0 +1,62 @@
+use sqlez_macros::sql;
+
+use crate::{define_connection, query};
+
+define_connection!(pub static ref KEY_VALUE_STORE: KeyValueStore<()> =
+    &[sql!(
+        CREATE TABLE IF NOT EXISTS kv_store(
+            key TEXT PRIMARY KEY,
+            value TEXT NOT NULL
+        ) STRICT;
+    )];
+);
+
+impl KeyValueStore {
+    query! {
+        pub fn read_kvp(key: &str) -> Result<Option<String>> {
+            SELECT value FROM kv_store WHERE key = (?)
+        }
+    }
+
+    query! {
+        pub async fn write_kvp(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 = (?)
+        }
+    }
+}
+
+// #[cfg(test)]
+// mod tests {
+//     use crate::kvp::KeyValueStore;
+
+//     #[gpui::test]
+//     async fn test_kvp() {
+//         let db = KeyValueStore(crate::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);
+//     }
+// }

crates/db2/src/query.rs 🔗

@@ -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>(indoc! { $sql })?()
+                .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
+        }
+    };
+}

crates/zed2/Cargo.toml 🔗

@@ -32,7 +32,7 @@ client2 = { path = "../client2" }
 # copilot = { path = "../copilot" }
 # copilot_button = { path = "../copilot_button" }
 # diagnostics = { path = "../diagnostics" }
-# db = { path = "../db" }
+db2 = { path = "../db2" }
 # editor = { path = "../editor" }
 # feedback = { path = "../feedback" }
 # file_finder = { path = "../file_finder" }

crates/zed2/src/main.rs 🔗

@@ -8,6 +8,7 @@ use cli::{
     ipc::{self, IpcSender},
     CliRequest, CliResponse, IpcHandshake, FORCE_CLI_MODE_ENV_VAR_NAME,
 };
+use db2::kvp::KEY_VALUE_STORE;
 use fs::RealFs;
 use futures::{channel::mpsc, SinkExt, StreamExt};
 use gpui2::{App, AppContext, AssetSource, AsyncAppContext, SemanticVersion, Task};
@@ -35,7 +36,7 @@ use std::{
 };
 use util::{
     channel::{parse_zed_link, ReleaseChannel, RELEASE_CHANNEL},
-    http::HttpClient,
+    http::{self, HttpClient},
     paths, ResultExt,
 };
 use uuid::Uuid;
@@ -49,7 +50,7 @@ use zed2::{ensure_only_instance, AppState, Assets, IsOnlyInstance};
 mod open_listener;
 
 fn main() {
-    // let http = http::client();
+    let http = http::client();
     init_paths();
     init_logger();
 
@@ -60,9 +61,9 @@ fn main() {
     log::info!("========== starting zed ==========");
     let app = App::production(Arc::new(Assets));
 
-    // let installation_id = app.executor().block(installation_id()).ok();
-    // let session_id = Uuid::new_v4().to_string();
-    // init_panic_hook(&app, installation_id.clone(), session_id.clone());
+    let installation_id = app.executor().block(installation_id()).ok();
+    let session_id = Uuid::new_v4().to_string();
+    init_panic_hook(&app, installation_id.clone(), session_id.clone());
 
     load_embedded_fonts(&app);
 
@@ -107,7 +108,7 @@ fn main() {
         handle_settings_file_changes(user_settings_file_rx, cx);
         // handle_keymap_file_changes(user_keymap_file_rx, cx);
 
-        // let client = client::Client::new(http.clone(), cx);
+        let client = client2::Client::new(http.clone(), cx);
         // let mut languages = LanguageRegistry::new(login_shell_env_loaded);
         // let copilot_language_server_id = languages.next_language_server_id();
         // languages.set_executor(cx.background().clone());
@@ -204,7 +205,7 @@ fn main() {
                 listener.open_urls(urls)
             }
         } else {
-            // upload_previous_panics(http.clone(), cx);
+            upload_previous_panics(http.clone(), cx);
 
             // TODO Development mode that forces the CLI mode usually runs Zed binary as is instead
             // of an *app, hence gets no specific callbacks run. Emulate them here, if needed.
@@ -296,21 +297,21 @@ fn main() {
 //     Ok::<_, anyhow::Error>(())
 // }
 
-// async fn installation_id() -> Result<String> {
-//     let legacy_key_name = "device_id";
+async fn installation_id() -> Result<String> {
+    let legacy_key_name = "device_id";
 
-//     if let Ok(Some(installation_id)) = KEY_VALUE_STORE.read_kvp(legacy_key_name) {
-//         Ok(installation_id)
-//     } else {
-//         let installation_id = Uuid::new_v4().to_string();
+    if let Ok(Some(installation_id)) = KEY_VALUE_STORE.read_kvp(legacy_key_name) {
+        Ok(installation_id)
+    } else {
+        let installation_id = Uuid::new_v4().to_string();
 
-//         KEY_VALUE_STORE
-//             .write_kvp(legacy_key_name.to_string(), installation_id.clone())
-//             .await?;
+        KEY_VALUE_STORE
+            .write_kvp(legacy_key_name.to_string(), installation_id.clone())
+            .await?;
 
-//         Ok(installation_id)
-//     }
-// }
+        Ok(installation_id)
+    }
+}
 
 async fn restore_or_create_workspace(_app_state: &Arc<AppState>, mut _cx: AsyncAppContext) {
     todo!("workspace")