gpui: Remove dependency on sqlez (#3871)

Piotr Osiewicz created

This removes one of the path dependencies in gpui that's only really
needed by `workspace` (which can work around lack of these
implementations by itself). In theory it should also improve build
scheduling (as gpui doesn't have to wait for main dependency of sqlez -
libsqlite3 - to finish it's 25 seconds-long build in release), though in
practice I didn't notice a substantial improvement.
Moreover `sqlez` was unused by `settings` too, so that's removed
as well.

Release Notes:

- N/A

Change summary

Cargo.lock                                |   3 
crates/gpui/Cargo.toml                    |   1 
crates/gpui/src/geometry.rs               |  45 --------
crates/gpui/src/platform.rs               |  73 ------------
crates/settings/Cargo.toml                |   1 
crates/workspace/Cargo.toml               |   1 
crates/workspace/src/persistence.rs       | 136 +++++++++++++++++++++++-
crates/workspace/src/persistence/model.rs |  23 ++--
crates/workspace/src/workspace.rs         |  17 ++-
9 files changed, 158 insertions(+), 142 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -3066,7 +3066,6 @@ dependencies = [
  "slotmap",
  "smallvec",
  "smol",
- "sqlez",
  "sum_tree",
  "taffy",
  "thiserror",
@@ -6847,7 +6846,6 @@ dependencies = [
  "serde_json",
  "serde_json_lenient",
  "smallvec",
- "sqlez",
  "toml 0.5.11",
  "tree-sitter",
  "tree-sitter-json 0.19.0",
@@ -9440,6 +9438,7 @@ dependencies = [
  "serde_json",
  "settings",
  "smallvec",
+ "sqlez",
  "terminal",
  "theme",
  "ui",

crates/gpui/Cargo.toml 🔗

@@ -18,7 +18,6 @@ collections = { path = "../collections" }
 gpui_macros = { path = "../gpui_macros" }
 util = { path = "../util" }
 sum_tree = { path = "../sum_tree" }
-sqlez = { path = "../sqlez" }
 async-task = "4.7"
 backtrace = { version = "0.3", optional = true }
 ctor.workspace = true

crates/gpui/src/geometry.rs 🔗

@@ -31,39 +31,6 @@ pub trait Along {
     fn apply_along(&self, axis: Axis, f: impl FnOnce(Self::Unit) -> Self::Unit) -> Self;
 }
 
-impl sqlez::bindable::StaticColumnCount for Axis {}
-impl sqlez::bindable::Bind for Axis {
-    fn bind(
-        &self,
-        statement: &sqlez::statement::Statement,
-        start_index: i32,
-    ) -> anyhow::Result<i32> {
-        match self {
-            Axis::Horizontal => "Horizontal",
-            Axis::Vertical => "Vertical",
-        }
-        .bind(statement, start_index)
-    }
-}
-
-impl sqlez::bindable::Column for Axis {
-    fn column(
-        statement: &mut sqlez::statement::Statement,
-        start_index: i32,
-    ) -> anyhow::Result<(Self, i32)> {
-        String::column(statement, start_index).and_then(|(axis_text, next_index)| {
-            Ok((
-                match axis_text.as_str() {
-                    "Horizontal" => Axis::Horizontal,
-                    "Vertical" => Axis::Vertical,
-                    _ => anyhow::bail!("Stored serialized item kind is incorrect"),
-                },
-                next_index,
-            ))
-        })
-    }
-}
-
 /// Describes a location in a 2D cartesian coordinate space.
 ///
 /// It holds two public fields, `x` and `y`, which represent the coordinates in the space.
@@ -2296,18 +2263,6 @@ impl From<f64> for GlobalPixels {
     }
 }
 
-impl sqlez::bindable::StaticColumnCount for GlobalPixels {}
-
-impl sqlez::bindable::Bind for GlobalPixels {
-    fn bind(
-        &self,
-        statement: &sqlez::statement::Statement,
-        start_index: i32,
-    ) -> anyhow::Result<i32> {
-        self.0.bind(statement, start_index)
-    }
-}
-
 /// Represents a length in rems, a unit based on the font-size of the window, which can be assigned with [WindowContext::set_rem_size].
 ///
 /// Rems are used for defining lengths that are scalable and consistent across different UI elements.

crates/gpui/src/platform.rs 🔗

@@ -6,19 +6,17 @@ mod mac;
 mod test;
 
 use crate::{
-    point, size, Action, AnyWindowHandle, BackgroundExecutor, Bounds, DevicePixels, Font, FontId,
-    FontMetrics, FontRun, ForegroundExecutor, GlobalPixels, GlyphId, InputEvent, Keymap,
-    LineLayout, Pixels, Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result,
-    Scene, SharedString, Size, TaskLabel,
+    Action, AnyWindowHandle, BackgroundExecutor, Bounds, DevicePixels, Font, FontId, FontMetrics,
+    FontRun, ForegroundExecutor, GlobalPixels, GlyphId, InputEvent, Keymap, LineLayout, Pixels,
+    Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result, Scene, SharedString,
+    Size, TaskLabel,
 };
-use anyhow::{anyhow, bail};
+use anyhow::anyhow;
 use async_task::Runnable;
 use futures::channel::oneshot;
 use parking::Unparker;
 use seahash::SeaHasher;
 use serde::{Deserialize, Serialize};
-use sqlez::bindable::{Bind, Column, StaticColumnCount};
-use sqlez::statement::Statement;
 use std::borrow::Cow;
 use std::hash::{Hash, Hasher};
 use std::time::Duration;
@@ -396,67 +394,6 @@ pub enum WindowBounds {
     Fixed(Bounds<GlobalPixels>),
 }
 
-impl StaticColumnCount for WindowBounds {
-    fn column_count() -> usize {
-        5
-    }
-}
-
-impl Bind for WindowBounds {
-    fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
-        let (region, next_index) = match self {
-            WindowBounds::Fullscreen => {
-                let next_index = statement.bind(&"Fullscreen", start_index)?;
-                (None, next_index)
-            }
-            WindowBounds::Maximized => {
-                let next_index = statement.bind(&"Maximized", start_index)?;
-                (None, next_index)
-            }
-            WindowBounds::Fixed(region) => {
-                let next_index = statement.bind(&"Fixed", start_index)?;
-                (Some(*region), next_index)
-            }
-        };
-
-        statement.bind(
-            &region.map(|region| {
-                (
-                    region.origin.x,
-                    region.origin.y,
-                    region.size.width,
-                    region.size.height,
-                )
-            }),
-            next_index,
-        )
-    }
-}
-
-impl Column for WindowBounds {
-    fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
-        let (window_state, next_index) = String::column(statement, start_index)?;
-        let bounds = match window_state.as_str() {
-            "Fullscreen" => WindowBounds::Fullscreen,
-            "Maximized" => WindowBounds::Maximized,
-            "Fixed" => {
-                let ((x, y, width, height), _) = Column::column(statement, next_index)?;
-                let x: f64 = x;
-                let y: f64 = y;
-                let width: f64 = width;
-                let height: f64 = height;
-                WindowBounds::Fixed(Bounds {
-                    origin: point(x.into(), y.into()),
-                    size: size(width.into(), height.into()),
-                })
-            }
-            _ => bail!("Window State did not have a valid string"),
-        };
-
-        Ok((bounds, next_index + 4))
-    }
-}
-
 #[derive(Copy, Clone, Debug)]
 pub enum WindowAppearance {
     Light,

crates/settings/Cargo.toml 🔗

@@ -14,7 +14,6 @@ test-support = ["gpui/test-support", "fs/test-support"]
 [dependencies]
 collections = { path = "../collections" }
 gpui = { path = "../gpui" }
-sqlez = { path = "../sqlez" }
 fs = { path = "../fs" }
 feature_flags = { path = "../feature_flags" }
 util = { path = "../util" }

crates/workspace/Cargo.toml 🔗

@@ -32,6 +32,7 @@ language = { path = "../language" }
 node_runtime = { path = "../node_runtime" }
 project = { path = "../project" }
 settings = { path = "../settings" }
+sqlez = { path = "../sqlez" }
 terminal = { path = "../terminal" }
 theme = { path = "../theme" }
 util = { path = "../util" }

crates/workspace/src/persistence.rs 🔗

@@ -6,7 +6,12 @@ use std::path::Path;
 
 use anyhow::{anyhow, bail, Context, Result};
 use db::{define_connection, query, sqlez::connection::Connection, sqlez_macros::sql};
-use gpui::{Axis, WindowBounds};
+use gpui::{point, size, Axis, Bounds, WindowBounds};
+
+use sqlez::{
+    bindable::{Bind, Column, StaticColumnCount},
+    statement::Statement,
+};
 
 use util::{unzip_option, ResultExt};
 use uuid::Uuid;
@@ -20,6 +25,121 @@ use model::{
 
 use self::model::DockStructure;
 
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub(crate) struct SerializedAxis(pub(crate) gpui::Axis);
+impl sqlez::bindable::StaticColumnCount for SerializedAxis {}
+impl sqlez::bindable::Bind for SerializedAxis {
+    fn bind(
+        &self,
+        statement: &sqlez::statement::Statement,
+        start_index: i32,
+    ) -> anyhow::Result<i32> {
+        match self.0 {
+            gpui::Axis::Horizontal => "Horizontal",
+            gpui::Axis::Vertical => "Vertical",
+        }
+        .bind(statement, start_index)
+    }
+}
+
+impl sqlez::bindable::Column for SerializedAxis {
+    fn column(
+        statement: &mut sqlez::statement::Statement,
+        start_index: i32,
+    ) -> anyhow::Result<(Self, i32)> {
+        String::column(statement, start_index).and_then(|(axis_text, next_index)| {
+            Ok((
+                match axis_text.as_str() {
+                    "Horizontal" => Self(Axis::Horizontal),
+                    "Vertical" => Self(Axis::Vertical),
+                    _ => anyhow::bail!("Stored serialized item kind is incorrect"),
+                },
+                next_index,
+            ))
+        })
+    }
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub(crate) struct SerializedWindowsBounds(pub(crate) WindowBounds);
+
+impl StaticColumnCount for SerializedWindowsBounds {
+    fn column_count() -> usize {
+        5
+    }
+}
+
+impl Bind for SerializedWindowsBounds {
+    fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
+        let (region, next_index) = match self.0 {
+            WindowBounds::Fullscreen => {
+                let next_index = statement.bind(&"Fullscreen", start_index)?;
+                (None, next_index)
+            }
+            WindowBounds::Maximized => {
+                let next_index = statement.bind(&"Maximized", start_index)?;
+                (None, next_index)
+            }
+            WindowBounds::Fixed(region) => {
+                let next_index = statement.bind(&"Fixed", start_index)?;
+                (Some(region), next_index)
+            }
+        };
+
+        statement.bind(
+            &region.map(|region| {
+                (
+                    SerializedGlobalPixels(region.origin.x),
+                    SerializedGlobalPixels(region.origin.y),
+                    SerializedGlobalPixels(region.size.width),
+                    SerializedGlobalPixels(region.size.height),
+                )
+            }),
+            next_index,
+        )
+    }
+}
+
+impl Column for SerializedWindowsBounds {
+    fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
+        let (window_state, next_index) = String::column(statement, start_index)?;
+        let bounds = match window_state.as_str() {
+            "Fullscreen" => SerializedWindowsBounds(WindowBounds::Fullscreen),
+            "Maximized" => SerializedWindowsBounds(WindowBounds::Maximized),
+            "Fixed" => {
+                let ((x, y, width, height), _) = Column::column(statement, next_index)?;
+                let x: f64 = x;
+                let y: f64 = y;
+                let width: f64 = width;
+                let height: f64 = height;
+                SerializedWindowsBounds(WindowBounds::Fixed(Bounds {
+                    origin: point(x.into(), y.into()),
+                    size: size(width.into(), height.into()),
+                }))
+            }
+            _ => bail!("Window State did not have a valid string"),
+        };
+
+        Ok((bounds, next_index + 4))
+    }
+}
+
+#[derive(Clone, Debug, PartialEq)]
+struct SerializedGlobalPixels(gpui::GlobalPixels);
+impl sqlez::bindable::StaticColumnCount for SerializedGlobalPixels {}
+
+impl sqlez::bindable::Bind for SerializedGlobalPixels {
+    fn bind(
+        &self,
+        statement: &sqlez::statement::Statement,
+        start_index: i32,
+    ) -> anyhow::Result<i32> {
+        let this: f64 = self.0.into();
+        let this: f32 = this as _;
+        this.bind(statement, start_index)
+    }
+}
+
 define_connection! {
     // Current schema shape using pseudo-rust syntax:
     //
@@ -181,7 +301,7 @@ impl WorkspaceDb {
     /// Returns a serialized workspace for the given worktree_roots. If the passed array
     /// is empty, the most recent workspace is returned instead. If no workspace for the
     /// passed roots is stored, returns none.
-    pub fn workspace_for_roots<P: AsRef<Path>>(
+    pub(crate) fn workspace_for_roots<P: AsRef<Path>>(
         &self,
         worktree_roots: &[P],
     ) -> Option<SerializedWorkspace> {
@@ -192,7 +312,7 @@ impl WorkspaceDb {
         let (workspace_id, workspace_location, bounds, display, docks): (
             WorkspaceId,
             WorkspaceLocation,
-            Option<WindowBounds>,
+            Option<SerializedWindowsBounds>,
             Option<Uuid>,
             DockStructure,
         ) = self
@@ -230,7 +350,7 @@ impl WorkspaceDb {
                 .get_center_pane_group(workspace_id)
                 .context("Getting center group")
                 .log_err()?,
-            bounds,
+            bounds: bounds.map(|bounds| bounds.0),
             display,
             docks,
         })
@@ -238,7 +358,7 @@ impl WorkspaceDb {
 
     /// Saves a workspace using the worktree roots. Will garbage collect any workspaces
     /// that used this workspace previously
-    pub async fn save_workspace(&self, workspace: SerializedWorkspace) {
+    pub(crate) async fn save_workspace(&self, workspace: SerializedWorkspace) {
         self.write(move |conn| {
             conn.with_savepoint("update_worktrees", || {
                 // Clear out panes and pane_groups
@@ -367,7 +487,7 @@ impl WorkspaceDb {
         type GroupKey = (Option<GroupId>, WorkspaceId);
         type GroupOrPane = (
             Option<GroupId>,
-            Option<Axis>,
+            Option<SerializedAxis>,
             Option<PaneId>,
             Option<bool>,
             Option<String>,
@@ -536,7 +656,7 @@ impl WorkspaceDb {
     }
 
     query! {
-        pub async fn set_window_bounds(workspace_id: WorkspaceId, bounds: WindowBounds, display: Uuid) -> Result<()> {
+        pub(crate) async fn set_window_bounds(workspace_id: WorkspaceId, bounds: SerializedWindowsBounds, display: Uuid) -> Result<()> {
             UPDATE workspaces
             SET window_state = ?2,
                 window_x = ?3,
@@ -683,7 +803,7 @@ mod tests {
 
     fn group(axis: Axis, children: Vec<SerializedPaneGroup>) -> SerializedPaneGroup {
         SerializedPaneGroup::Group {
-            axis,
+            axis: SerializedAxis(axis),
             flexes: None,
             children,
         }

crates/workspace/src/persistence/model.rs 🔗

@@ -1,3 +1,4 @@
+use super::SerializedAxis;
 use crate::{item::ItemHandle, ItemDeserializers, Member, Pane, PaneAxis, Workspace, WorkspaceId};
 use anyhow::{Context, Result};
 use async_recursion::async_recursion;
@@ -5,7 +6,7 @@ use db::sqlez::{
     bindable::{Bind, Column, StaticColumnCount},
     statement::Statement,
 };
-use gpui::{AsyncWindowContext, Axis, Model, Task, View, WeakView, WindowBounds};
+use gpui::{AsyncWindowContext, Model, Task, View, WeakView, WindowBounds};
 use project::Project;
 use std::{
     path::{Path, PathBuf},
@@ -54,13 +55,13 @@ impl Column for WorkspaceLocation {
 }
 
 #[derive(Debug, PartialEq, Clone)]
-pub struct SerializedWorkspace {
-    pub id: WorkspaceId,
-    pub location: WorkspaceLocation,
-    pub center_group: SerializedPaneGroup,
-    pub bounds: Option<WindowBounds>,
-    pub display: Option<Uuid>,
-    pub docks: DockStructure,
+pub(crate) struct SerializedWorkspace {
+    pub(crate) id: WorkspaceId,
+    pub(crate) location: WorkspaceLocation,
+    pub(crate) center_group: SerializedPaneGroup,
+    pub(crate) bounds: Option<WindowBounds>,
+    pub(crate) display: Option<Uuid>,
+    pub(crate) docks: DockStructure,
 }
 
 #[derive(Debug, PartialEq, Clone, Default)]
@@ -126,9 +127,9 @@ impl Bind for DockData {
 }
 
 #[derive(Debug, PartialEq, Clone)]
-pub enum SerializedPaneGroup {
+pub(crate) enum SerializedPaneGroup {
     Group {
-        axis: Axis,
+        axis: SerializedAxis,
         flexes: Option<Vec<f32>>,
         children: Vec<SerializedPaneGroup>,
     },
@@ -183,7 +184,7 @@ impl SerializedPaneGroup {
                 }
 
                 Some((
-                    Member::Axis(PaneAxis::load(axis, members, flexes)),
+                    Member::Axis(PaneAxis::load(axis.0, members, flexes)),
                     current_active_pane,
                     items,
                 ))

crates/workspace/src/workspace.rs 🔗

@@ -42,9 +42,9 @@ use node_runtime::NodeRuntime;
 use notifications::{simple_message_notification::MessageNotification, NotificationHandle};
 pub use pane::*;
 pub use pane_group::*;
-use persistence::DB;
+use persistence::{model::SerializedWorkspace, SerializedWindowsBounds, DB};
 pub use persistence::{
-    model::{ItemId, SerializedWorkspace, WorkspaceLocation},
+    model::{ItemId, WorkspaceLocation},
     WorkspaceDb, DB as WORKSPACE_DB,
 };
 use postage::stream::Stream;
@@ -70,8 +70,9 @@ use util::ResultExt;
 use uuid::Uuid;
 pub use workspace_settings::{AutosaveSetting, WorkspaceSettings};
 
-use crate::persistence::model::{
-    DockData, DockStructure, SerializedItem, SerializedPane, SerializedPaneGroup,
+use crate::persistence::{
+    model::{DockData, DockStructure, SerializedItem, SerializedPane, SerializedPaneGroup},
+    SerializedAxis,
 };
 
 lazy_static! {
@@ -625,7 +626,11 @@ impl Workspace {
 
                     if let Some(display_uuid) = display.uuid().log_err() {
                         cx.background_executor()
-                            .spawn(DB.set_window_bounds(workspace_id, bounds, display_uuid))
+                            .spawn(DB.set_window_bounds(
+                                workspace_id,
+                                SerializedWindowsBounds(bounds),
+                                display_uuid,
+                            ))
                             .detach_and_log_err(cx);
                     }
                 }
@@ -2989,7 +2994,7 @@ impl Workspace {
                     flexes,
                     bounding_boxes: _,
                 }) => SerializedPaneGroup::Group {
-                    axis: *axis,
+                    axis: SerializedAxis(*axis),
                     children: members
                         .iter()
                         .map(|member| build_serialized_pane_group(member, cx))