model.rs

  1use std::{
  2    path::{Path, PathBuf},
  3    sync::Arc,
  4};
  5
  6use anyhow::{bail, Result};
  7
  8use gpui::Axis;
  9use settings::DockAnchor;
 10use sqlez::{
 11    bindable::{Bind, Column},
 12    statement::Statement,
 13};
 14
 15#[derive(Debug, Clone, PartialEq, Eq)]
 16pub(crate) struct WorkspaceId(Vec<PathBuf>);
 17
 18impl WorkspaceId {
 19    pub fn paths(self) -> Vec<PathBuf> {
 20        self.0
 21    }
 22}
 23
 24impl<P: AsRef<Path>, T: IntoIterator<Item = P>> From<T> for WorkspaceId {
 25    fn from(iterator: T) -> Self {
 26        let mut roots = iterator
 27            .into_iter()
 28            .map(|p| p.as_ref().to_path_buf())
 29            .collect::<Vec<_>>();
 30        roots.sort();
 31        Self(roots)
 32    }
 33}
 34
 35impl Bind for &WorkspaceId {
 36    fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
 37        bincode::serialize(&self.0)
 38            .expect("Bincode serialization of paths should not fail")
 39            .bind(statement, start_index)
 40    }
 41}
 42
 43impl Column for WorkspaceId {
 44    fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
 45        let blob = statement.column_blob(start_index)?;
 46        Ok((WorkspaceId(bincode::deserialize(blob)?), start_index + 1))
 47    }
 48}
 49
 50#[derive(Debug, PartialEq, Eq)]
 51pub struct SerializedWorkspace {
 52    pub dock_anchor: DockAnchor,
 53    pub dock_visible: bool,
 54    pub center_group: SerializedPaneGroup,
 55    pub dock_pane: SerializedPane,
 56}
 57
 58#[derive(Debug, PartialEq, Eq, Clone)]
 59pub enum SerializedPaneGroup {
 60    Group {
 61        axis: Axis,
 62        children: Vec<SerializedPaneGroup>,
 63    },
 64    Pane(SerializedPane),
 65}
 66
 67impl Default for SerializedPaneGroup {
 68    fn default() -> Self {
 69        Self::Group {
 70            axis: Axis::Horizontal,
 71            children: vec![Self::Pane(Default::default())],
 72        }
 73    }
 74}
 75
 76#[derive(Debug, PartialEq, Eq, Default, Clone)]
 77pub struct SerializedPane {
 78    pub(crate) children: Vec<SerializedItem>,
 79}
 80
 81impl SerializedPane {
 82    pub fn new(children: Vec<SerializedItem>) -> Self {
 83        SerializedPane { children }
 84    }
 85}
 86
 87pub type GroupId = i64;
 88pub type PaneId = i64;
 89pub type ItemId = usize;
 90
 91pub(crate) enum SerializedItemKind {
 92    Editor,
 93    Diagnostics,
 94    ProjectSearch,
 95    Terminal,
 96}
 97
 98impl Bind for SerializedItemKind {
 99    fn bind(&self, statement: &Statement, start_index: i32) -> anyhow::Result<i32> {
100        match self {
101            SerializedItemKind::Editor => "Editor",
102            SerializedItemKind::Diagnostics => "Diagnostics",
103            SerializedItemKind::ProjectSearch => "ProjectSearch",
104            SerializedItemKind::Terminal => "Terminal",
105        }
106        .bind(statement, start_index)
107    }
108}
109
110impl Column for SerializedItemKind {
111    fn column(statement: &mut Statement, start_index: i32) -> anyhow::Result<(Self, i32)> {
112        String::column(statement, start_index).and_then(|(kind_text, next_index)| {
113            Ok((
114                match kind_text.as_ref() {
115                    "Editor" => SerializedItemKind::Editor,
116                    "Diagnostics" => SerializedItemKind::Diagnostics,
117                    "ProjectSearch" => SerializedItemKind::ProjectSearch,
118                    "Terminal" => SerializedItemKind::Terminal,
119                    _ => bail!("Stored serialized item kind is incorrect"),
120                },
121                next_index,
122            ))
123        })
124    }
125}
126
127#[derive(Debug, PartialEq, Eq, Clone)]
128pub enum SerializedItem {
129    Editor { item_id: usize, path: Arc<Path> },
130    Diagnostics { item_id: usize },
131    ProjectSearch { item_id: usize, query: String },
132    Terminal { item_id: usize },
133}
134
135impl SerializedItem {
136    pub fn item_id(&self) -> usize {
137        match self {
138            SerializedItem::Editor { item_id, .. } => *item_id,
139            SerializedItem::Diagnostics { item_id } => *item_id,
140            SerializedItem::ProjectSearch { item_id, .. } => *item_id,
141            SerializedItem::Terminal { item_id } => *item_id,
142        }
143    }
144
145    pub(crate) fn kind(&self) -> SerializedItemKind {
146        match self {
147            SerializedItem::Editor { .. } => SerializedItemKind::Editor,
148            SerializedItem::Diagnostics { .. } => SerializedItemKind::Diagnostics,
149            SerializedItem::ProjectSearch { .. } => SerializedItemKind::ProjectSearch,
150            SerializedItem::Terminal { .. } => SerializedItemKind::Terminal,
151        }
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use sqlez::connection::Connection;
158
159    use crate::persistence::model::DockAnchor;
160
161    use super::WorkspaceId;
162
163    #[test]
164    fn test_workspace_round_trips() {
165        let db = Connection::open_memory("workspace_id_round_trips");
166
167        db.exec(indoc::indoc! {"
168                CREATE TABLE workspace_id_test(
169                workspace_id BLOB,
170                dock_anchor TEXT
171                );"})
172            .unwrap()()
173        .unwrap();
174
175        let workspace_id: WorkspaceId = WorkspaceId::from(&["\test2", "\test1"]);
176
177        db.exec_bound("INSERT INTO workspace_id_test(workspace_id, dock_anchor) VALUES (?,?)")
178            .unwrap()((&workspace_id, DockAnchor::Bottom))
179        .unwrap();
180
181        assert_eq!(
182            db.select_row("SELECT workspace_id, dock_anchor FROM workspace_id_test LIMIT 1")
183                .unwrap()()
184            .unwrap(),
185            Some((WorkspaceId::from(&["\test1", "\test2"]), DockAnchor::Bottom))
186        );
187    }
188}