Refined sqlez, implemented 60% of workspace serialization sql
This commit is contained in:
parent
6b214acbc4
commit
0186289420
11 changed files with 569 additions and 433 deletions
|
@ -1,4 +1,4 @@
|
|||
mod items;
|
||||
pub(crate) mod items;
|
||||
pub mod model;
|
||||
pub(crate) mod pane;
|
||||
|
||||
|
@ -58,8 +58,14 @@ impl Db {
|
|||
.flatten()?;
|
||||
|
||||
Some(SerializedWorkspace {
|
||||
dock_pane: self.get_dock_pane(&workspace_id)?,
|
||||
center_group: self.get_center_group(&workspace_id),
|
||||
dock_pane: self
|
||||
.get_dock_pane(&workspace_id)
|
||||
.context("Getting dock pane")
|
||||
.log_err()?,
|
||||
center_group: self
|
||||
.get_center_group(&workspace_id)
|
||||
.context("Getting center group")
|
||||
.log_err()?,
|
||||
dock_anchor,
|
||||
dock_visible,
|
||||
})
|
||||
|
@ -70,231 +76,152 @@ impl Db {
|
|||
pub fn save_workspace<P: AsRef<Path>>(
|
||||
&self,
|
||||
worktree_roots: &[P],
|
||||
workspace: SerializedWorkspace,
|
||||
old_roots: Option<&[P]>,
|
||||
workspace: &SerializedWorkspace,
|
||||
) {
|
||||
let workspace_id: WorkspaceId = worktree_roots.into();
|
||||
|
||||
self.with_savepoint("update_worktrees", |conn| {
|
||||
self.with_savepoint("update_worktrees", || {
|
||||
if let Some(old_roots) = old_roots {
|
||||
let old_id: WorkspaceId = old_roots.into();
|
||||
|
||||
self.prepare("DELETE FROM WORKSPACES WHERE workspace_id = ?")?
|
||||
.with_bindings(&old_id)?
|
||||
.exec()?;
|
||||
}
|
||||
|
||||
// Delete any previous workspaces with the same roots. This cascades to all
|
||||
// other tables that are based on the same roots set.
|
||||
// Insert new workspace into workspaces table if none were found
|
||||
self.prepare(indoc!{"
|
||||
DELETE FROM workspaces WHERE workspace_id = ?1;
|
||||
INSERT INTO workspaces(workspace_id, dock_anchor, dock_visible) VALUES (?1, ?, ?)"})?
|
||||
self.prepare("DELETE FROM workspaces WHERE workspace_id = ?;")?
|
||||
.with_bindings(&workspace_id)?
|
||||
.exec()?;
|
||||
|
||||
self.prepare(
|
||||
"INSERT INTO workspaces(workspace_id, dock_anchor, dock_visible) VALUES (?, ?, ?)",
|
||||
)?
|
||||
.with_bindings((&workspace_id, workspace.dock_anchor, workspace.dock_visible))?
|
||||
.exec()?;
|
||||
|
||||
|
||||
// Save center pane group and dock pane
|
||||
Self::save_center_group(&workspace_id, &workspace.center_group, conn)?;
|
||||
Self::save_dock_pane(&workspace_id, &workspace.dock_pane, conn)?;
|
||||
self.save_center_group(&workspace_id, &workspace.center_group)?;
|
||||
self.save_dock_pane(&workspace_id, &workspace.dock_pane)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.with_context(|| format!("Update workspace with roots {:?}", worktree_roots.iter().map(|p| p.as_ref()).collect::<Vec<_>>()))
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Update workspace with roots {:?}",
|
||||
worktree_roots
|
||||
.iter()
|
||||
.map(|p| p.as_ref())
|
||||
.collect::<Vec<_>>()
|
||||
)
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
|
||||
/// Returns the previous workspace ids sorted by last modified along with their opened worktree roots
|
||||
pub fn recent_workspaces(&self, limit: usize) -> Vec<Vec<PathBuf>> {
|
||||
iife!({
|
||||
Ok::<_, anyhow::Error>(self.prepare("SELECT workspace_id FROM workspaces ORDER BY timestamp DESC LIMIT ?")?
|
||||
// TODO, upgrade anyhow: https://docs.rs/anyhow/1.0.66/anyhow/fn.Ok.html
|
||||
Ok::<_, anyhow::Error>(
|
||||
self.prepare(
|
||||
"SELECT workspace_id FROM workspaces ORDER BY timestamp DESC LIMIT ?",
|
||||
)?
|
||||
.with_bindings(limit)?
|
||||
.rows::<WorkspaceId>()?
|
||||
.into_iter().map(|id| id.0)
|
||||
.collect::<Vec<Vec<PathBuf>>>())
|
||||
|
||||
}).log_err().unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|id| id.paths())
|
||||
.collect::<Vec<Vec<PathBuf>>>(),
|
||||
)
|
||||
})
|
||||
.log_err()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
model::{
|
||||
DockAnchor::{Bottom, Expanded, Right},
|
||||
SerializedWorkspace,
|
||||
},
|
||||
Db,
|
||||
};
|
||||
|
||||
// use std::{path::PathBuf, thread::sleep, time::Duration};
|
||||
#[test]
|
||||
fn test_basic_functionality() {
|
||||
env_logger::init();
|
||||
|
||||
// use crate::Db;
|
||||
let db = Db::open_in_memory("test_basic_functionality");
|
||||
|
||||
// use super::WorkspaceId;
|
||||
let workspace_1 = SerializedWorkspace {
|
||||
dock_anchor: Bottom,
|
||||
dock_visible: true,
|
||||
center_group: Default::default(),
|
||||
dock_pane: Default::default(),
|
||||
};
|
||||
|
||||
// #[test]
|
||||
// fn test_workspace_saving() {
|
||||
// env_logger::init();
|
||||
// let db = Db::open_in_memory("test_new_worktrees_for_roots");
|
||||
let workspace_2 = SerializedWorkspace {
|
||||
dock_anchor: Expanded,
|
||||
dock_visible: false,
|
||||
center_group: Default::default(),
|
||||
dock_pane: Default::default(),
|
||||
};
|
||||
|
||||
// // Test nothing returned with no roots at first
|
||||
// assert_eq!(db.workspace_for_roots::<String>(&[]), None);
|
||||
let workspace_3 = SerializedWorkspace {
|
||||
dock_anchor: Right,
|
||||
dock_visible: true,
|
||||
center_group: Default::default(),
|
||||
dock_pane: Default::default(),
|
||||
};
|
||||
|
||||
// // Test creation
|
||||
// let workspace_1 = db.workspace_for_roots::<String>(&[]);
|
||||
// assert_eq!(workspace_1.workspace_id, WorkspaceId(1));
|
||||
db.save_workspace(&["/tmp", "/tmp2"], None, &workspace_1);
|
||||
db.save_workspace(&["/tmp"], None, &workspace_2);
|
||||
|
||||
// // Ensure the timestamps are different
|
||||
// sleep(Duration::from_secs(1));
|
||||
// db.make_new_workspace::<String>(&[]);
|
||||
db.write_to("test.db").unwrap();
|
||||
|
||||
// // Test pulling another value from recent workspaces
|
||||
// let workspace_2 = db.workspace_for_roots::<String>(&[]);
|
||||
// assert_eq!(workspace_2.workspace_id, WorkspaceId(2));
|
||||
// Test that paths are treated as a set
|
||||
assert_eq!(
|
||||
db.workspace_for_roots(&["/tmp", "/tmp2"]).unwrap(),
|
||||
workspace_1
|
||||
);
|
||||
assert_eq!(
|
||||
db.workspace_for_roots(&["/tmp2", "/tmp"]).unwrap(),
|
||||
workspace_1
|
||||
);
|
||||
|
||||
// // Ensure the timestamps are different
|
||||
// sleep(Duration::from_secs(1));
|
||||
// Make sure that other keys work
|
||||
assert_eq!(db.workspace_for_roots(&["/tmp"]).unwrap(), workspace_2);
|
||||
assert_eq!(db.workspace_for_roots(&["/tmp3", "/tmp2", "/tmp4"]), None);
|
||||
|
||||
// // Test creating a new workspace that doesn't exist already
|
||||
// let workspace_3 = db.workspace_for_roots(&["/tmp", "/tmp2"]);
|
||||
// assert_eq!(workspace_3.workspace_id, WorkspaceId(3));
|
||||
// Test 'mutate' case of updating a pre-existing id
|
||||
db.save_workspace(&["/tmp", "/tmp2"], Some(&["/tmp", "/tmp2"]), &workspace_2);
|
||||
assert_eq!(
|
||||
db.workspace_for_roots(&["/tmp", "/tmp2"]).unwrap(),
|
||||
workspace_2
|
||||
);
|
||||
|
||||
// // Make sure it's in the recent workspaces....
|
||||
// let workspace_3 = db.workspace_for_roots::<String>(&[]);
|
||||
// assert_eq!(workspace_3.workspace_id, WorkspaceId(3));
|
||||
// Test other mechanism for mutating
|
||||
db.save_workspace(&["/tmp", "/tmp2"], None, &workspace_3);
|
||||
assert_eq!(
|
||||
db.workspace_for_roots(&["/tmp", "/tmp2"]).unwrap(),
|
||||
workspace_3
|
||||
);
|
||||
|
||||
// // And that it can be pulled out again
|
||||
// let workspace_3 = db.workspace_for_roots(&["/tmp", "/tmp2"]);
|
||||
// assert_eq!(workspace_3.workspace_id, WorkspaceId(3));
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn test_empty_worktrees() {
|
||||
// let db = Db::open_in_memory("test_empty_worktrees");
|
||||
|
||||
// assert_eq!(None, db.workspace::<String>(&[]));
|
||||
|
||||
// db.make_new_workspace::<String>(&[]); //ID 1
|
||||
// db.make_new_workspace::<String>(&[]); //ID 2
|
||||
// db.update_worktrees(&WorkspaceId(1), &["/tmp", "/tmp2"]);
|
||||
|
||||
// // Sanity check
|
||||
// assert_eq!(db.workspace(&["/tmp", "/tmp2"]).unwrap().0, WorkspaceId(1));
|
||||
|
||||
// db.update_worktrees::<String>(&WorkspaceId(1), &[]);
|
||||
|
||||
// // Make sure 'no worktrees' fails correctly. returning [1, 2] from this
|
||||
// // call would be semantically correct (as those are the workspaces that
|
||||
// // don't have roots) but I'd prefer that this API to either return exactly one
|
||||
// // workspace, and None otherwise
|
||||
// assert_eq!(db.workspace::<String>(&[]), None,);
|
||||
|
||||
// assert_eq!(db.last_workspace().unwrap().0, WorkspaceId(1));
|
||||
|
||||
// assert_eq!(
|
||||
// db.recent_workspaces(2),
|
||||
// vec![Vec::<PathBuf>::new(), Vec::<PathBuf>::new()],
|
||||
// )
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn test_more_workspace_ids() {
|
||||
// let data = &[
|
||||
// (WorkspaceId(1), vec!["/tmp1"]),
|
||||
// (WorkspaceId(2), vec!["/tmp1", "/tmp2"]),
|
||||
// (WorkspaceId(3), vec!["/tmp1", "/tmp2", "/tmp3"]),
|
||||
// (WorkspaceId(4), vec!["/tmp2", "/tmp3"]),
|
||||
// (WorkspaceId(5), vec!["/tmp2", "/tmp3", "/tmp4"]),
|
||||
// (WorkspaceId(6), vec!["/tmp2", "/tmp4"]),
|
||||
// (WorkspaceId(7), vec!["/tmp2"]),
|
||||
// ];
|
||||
|
||||
// let db = Db::open_in_memory("test_more_workspace_ids");
|
||||
|
||||
// for (workspace_id, entries) in data {
|
||||
// db.make_new_workspace::<String>(&[]);
|
||||
// db.update_worktrees(workspace_id, entries);
|
||||
// }
|
||||
|
||||
// assert_eq!(WorkspaceId(1), db.workspace(&["/tmp1"]).unwrap().0);
|
||||
// assert_eq!(db.workspace(&["/tmp1", "/tmp2"]).unwrap().0, WorkspaceId(2));
|
||||
// assert_eq!(
|
||||
// db.workspace(&["/tmp1", "/tmp2", "/tmp3"]).unwrap().0,
|
||||
// WorkspaceId(3)
|
||||
// );
|
||||
// assert_eq!(db.workspace(&["/tmp2", "/tmp3"]).unwrap().0, WorkspaceId(4));
|
||||
// assert_eq!(
|
||||
// db.workspace(&["/tmp2", "/tmp3", "/tmp4"]).unwrap().0,
|
||||
// WorkspaceId(5)
|
||||
// );
|
||||
// assert_eq!(db.workspace(&["/tmp2", "/tmp4"]).unwrap().0, WorkspaceId(6));
|
||||
// assert_eq!(db.workspace(&["/tmp2"]).unwrap().0, WorkspaceId(7));
|
||||
|
||||
// assert_eq!(db.workspace(&["/tmp1", "/tmp5"]), None);
|
||||
// assert_eq!(db.workspace(&["/tmp5"]), None);
|
||||
// assert_eq!(db.workspace(&["/tmp2", "/tmp3", "/tmp4", "/tmp5"]), None);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn test_detect_workspace_id() {
|
||||
// let data = &[
|
||||
// (WorkspaceId(1), vec!["/tmp"]),
|
||||
// (WorkspaceId(2), vec!["/tmp", "/tmp2"]),
|
||||
// (WorkspaceId(3), vec!["/tmp", "/tmp2", "/tmp3"]),
|
||||
// ];
|
||||
|
||||
// let db = Db::open_in_memory("test_detect_workspace_id");
|
||||
|
||||
// for (workspace_id, entries) in data {
|
||||
// db.make_new_workspace::<String>(&[]);
|
||||
// db.update_worktrees(workspace_id, entries);
|
||||
// }
|
||||
|
||||
// assert_eq!(db.workspace(&["/tmp2"]), None);
|
||||
// assert_eq!(db.workspace(&["/tmp2", "/tmp3"]), None);
|
||||
// assert_eq!(db.workspace(&["/tmp"]).unwrap().0, WorkspaceId(1));
|
||||
// assert_eq!(db.workspace(&["/tmp", "/tmp2"]).unwrap().0, WorkspaceId(2));
|
||||
// assert_eq!(
|
||||
// db.workspace(&["/tmp", "/tmp2", "/tmp3"]).unwrap().0,
|
||||
// WorkspaceId(3)
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn test_tricky_overlapping_updates() {
|
||||
// // DB state:
|
||||
// // (/tree) -> ID: 1
|
||||
// // (/tree, /tree2) -> ID: 2
|
||||
// // (/tree2, /tree3) -> ID: 3
|
||||
|
||||
// // -> User updates 2 to: (/tree2, /tree3)
|
||||
|
||||
// // DB state:
|
||||
// // (/tree) -> ID: 1
|
||||
// // (/tree2, /tree3) -> ID: 2
|
||||
// // Get rid of 3 for garbage collection
|
||||
|
||||
// let data = &[
|
||||
// (WorkspaceId(1), vec!["/tmp"]),
|
||||
// (WorkspaceId(2), vec!["/tmp", "/tmp2"]),
|
||||
// (WorkspaceId(3), vec!["/tmp2", "/tmp3"]),
|
||||
// ];
|
||||
|
||||
// let db = Db::open_in_memory("test_tricky_overlapping_update");
|
||||
|
||||
// // Load in the test data
|
||||
// for (workspace_id, entries) in data {
|
||||
// db.make_new_workspace::<String>(&[]);
|
||||
// db.update_worktrees(workspace_id, entries);
|
||||
// }
|
||||
|
||||
// sleep(Duration::from_secs(1));
|
||||
// // Execute the update
|
||||
// db.update_worktrees(&WorkspaceId(2), &["/tmp2", "/tmp3"]);
|
||||
|
||||
// // Make sure that workspace 3 doesn't exist
|
||||
// assert_eq!(db.workspace(&["/tmp2", "/tmp3"]).unwrap().0, WorkspaceId(2));
|
||||
|
||||
// // And that workspace 1 was untouched
|
||||
// assert_eq!(db.workspace(&["/tmp"]).unwrap().0, WorkspaceId(1));
|
||||
|
||||
// // And that workspace 2 is no longer registered under these roots
|
||||
// assert_eq!(db.workspace(&["/tmp", "/tmp2"]), None);
|
||||
|
||||
// assert_eq!(db.last_workspace().unwrap().0, WorkspaceId(2));
|
||||
|
||||
// let recent_workspaces = db.recent_workspaces(10);
|
||||
// assert_eq!(
|
||||
// recent_workspaces.get(0).unwrap(),
|
||||
// &vec![PathBuf::from("/tmp2"), PathBuf::from("/tmp3")]
|
||||
// );
|
||||
// assert_eq!(
|
||||
// recent_workspaces.get(1).unwrap(),
|
||||
// &vec![PathBuf::from("/tmp")]
|
||||
// );
|
||||
// }
|
||||
// Make sure that updating paths differently also works
|
||||
db.save_workspace(
|
||||
&["/tmp3", "/tmp4", "/tmp2"],
|
||||
Some(&["/tmp", "/tmp2"]),
|
||||
&workspace_3,
|
||||
);
|
||||
assert_eq!(db.workspace_for_roots(&["/tmp2", "tmp"]), None);
|
||||
assert_eq!(
|
||||
db.workspace_for_roots(&["/tmp2", "/tmp3", "/tmp4"])
|
||||
.unwrap(),
|
||||
workspace_3
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue