diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-04-03 02:37:57 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-04-03 02:37:57 +0300 |
| commit | a9b1ef64ddd8942525ba8989271b0c232aa686a4 (patch) | |
| tree | 350899d0dee21db91a90a678a0bf2ba0d1807b41 /src | |
| parent | 685862e276ef4911029e47bdffad79143b096d0a (diff) | |
chore: remove llm integration tests and prep release docs
Diffstat (limited to 'src')
| -rw-r--r-- | src/persist.rs | 160 |
1 files changed, 158 insertions, 2 deletions
diff --git a/src/persist.rs b/src/persist.rs index ba88df8e..c296b0ac 100644 --- a/src/persist.rs +++ b/src/persist.rs @@ -8,7 +8,7 @@ use std::sync::atomic::AtomicBool; use std::sync::Arc; use ratatui::layout::Direction; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use tracing::{error, info, warn}; use tokio::sync::{mpsc, Notify}; @@ -32,7 +32,7 @@ pub struct SessionSnapshot { pub selected: usize, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize)] pub struct WorkspaceSnapshot { #[serde(default)] pub custom_name: Option<String>, @@ -42,6 +42,41 @@ pub struct WorkspaceSnapshot { pub active_tab: usize, } +#[derive(Deserialize)] +struct WorkspaceSnapshotWire { + #[serde(default)] + custom_name: Option<String>, + #[serde(default)] + identity_cwd: Option<PathBuf>, + #[serde(default)] + tabs: Vec<TabSnapshot>, + #[serde(default)] + active_tab: usize, + #[serde(default)] + layout: Option<LayoutSnapshot>, + #[serde(default)] + panes: HashMap<u32, PaneSnapshot>, + #[serde(default)] + zoomed: Option<bool>, + #[serde(default)] + focused: Option<u32>, + #[serde(default)] + root_pane: Option<u32>, +} + +#[derive(Deserialize)] +struct LegacyWorkspaceSnapshot { + #[serde(default)] + custom_name: Option<String>, + layout: LayoutSnapshot, + panes: HashMap<u32, PaneSnapshot>, + zoomed: bool, + #[serde(default)] + focused: Option<u32>, + #[serde(default)] + root_pane: Option<u32>, +} + #[derive(Serialize, Deserialize)] pub struct TabSnapshot { #[serde(default)] @@ -78,6 +113,95 @@ pub enum DirectionSnapshot { Vertical, } +impl From<LegacyWorkspaceSnapshot> for WorkspaceSnapshot { + fn from(snap: LegacyWorkspaceSnapshot) -> Self { + let identity_cwd = legacy_identity_cwd(&snap); + let tab = TabSnapshot { + custom_name: None, + layout: snap.layout, + panes: snap.panes, + zoomed: snap.zoomed, + focused: snap.focused, + root_pane: snap.root_pane, + }; + + Self { + custom_name: snap.custom_name, + identity_cwd, + tabs: vec![tab], + active_tab: 0, + } + } +} + +impl<'de> Deserialize<'de> for WorkspaceSnapshot { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + let snap = WorkspaceSnapshotWire::deserialize(deserializer)?; + + if let Some(identity_cwd) = snap.identity_cwd { + return Ok(Self { + custom_name: snap.custom_name, + identity_cwd, + tabs: snap.tabs, + active_tab: snap.active_tab, + }); + } + + if let Some(layout) = snap.layout { + // Backward-compat path for pre-tab session snapshots (<= v0.2.4). + // Remove this migration once we intentionally drop support for v2 sessions. + return Ok(LegacyWorkspaceSnapshot { + custom_name: snap.custom_name, + layout, + panes: snap.panes, + zoomed: snap.zoomed.unwrap_or(false), + focused: snap.focused, + root_pane: snap.root_pane, + } + .into()); + } + + Err(<D::Error as serde::de::Error>::custom( + "workspace snapshot is neither current nor legacy format", + )) + } +} + +fn legacy_identity_cwd(snap: &LegacyWorkspaceSnapshot) -> PathBuf { + let root_pane = snap + .root_pane + .or_else(|| first_pane_id_in_layout(&snap.layout)); + + root_pane + .and_then(|pane_id| snap.panes.get(&pane_id)) + .map(|pane| pane.cwd.clone()) + .or_else(|| { + first_pane_id_in_layout(&snap.layout) + .and_then(|pane_id| snap.panes.get(&pane_id)) + .map(|pane| pane.cwd.clone()) + }) + .or_else(|| { + snap.panes + .keys() + .min() + .and_then(|pane_id| snap.panes.get(pane_id)) + .map(|pane| pane.cwd.clone()) + }) + .unwrap_or_else(|| std::env::current_dir().unwrap_or_else(|_| "/".into())) +} + +fn first_pane_id_in_layout(layout: &LayoutSnapshot) -> Option<u32> { + match layout { + LayoutSnapshot::Pane(id) => Some(*id), + LayoutSnapshot::Split { first, second, .. } => { + first_pane_id_in_layout(first).or_else(|| first_pane_id_in_layout(second)) + } + } +} + // --- Capture --- /// Capture the current app state into a serializable snapshot. @@ -513,6 +637,38 @@ mod tests { } #[test] + fn legacy_workspace_snapshot_migrates_to_single_tab() { + let json = serde_json::json!({ + "version": 2, + "workspaces": [{ + "custom_name": "legacy", + "layout": LayoutSnapshot::Pane(0), + "panes": { + "0": { "cwd": "/tmp/pion" } + }, + "zoomed": false, + "focused": 0, + "root_pane": 0 + }], + "active": 0, + "selected": 0 + }) + .to_string(); + + let snap: SessionSnapshot = serde_json::from_str(&json).unwrap(); + let ws = &snap.workspaces[0]; + + assert_eq!(snap.workspaces.len(), 1); + assert_eq!(ws.custom_name.as_deref(), Some("legacy")); + assert_eq!(ws.identity_cwd, PathBuf::from("/tmp/pion")); + assert_eq!(ws.active_tab, 0); + assert_eq!(ws.tabs.len(), 1); + assert_eq!(ws.tabs[0].focused, Some(0)); + assert_eq!(ws.tabs[0].root_pane, Some(0)); + assert_eq!(ws.tabs[0].panes[&0].cwd, PathBuf::from("/tmp/pion")); + } + + #[test] fn capture_and_restore_node_round_trip() { // Create a tree: Split(H, 0.5, Pane(0), Split(V, 0.3, Pane(1), Pane(2))) let node = Node::Split { |
