Skip to main content

wisp_core/
preview.rs

1use std::path::PathBuf;
2
3use crate::{Candidate, CandidateMetadata};
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
6pub enum PreviewKey {
7    Session(String),
8    Directory(PathBuf),
9    File(PathBuf),
10    Metadata(String),
11}
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum PreviewKind {
15    SessionSummary,
16    Directory,
17    File,
18    Metadata,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub enum PreviewRequest {
23    SessionSummary {
24        key: PreviewKey,
25        session_name: String,
26    },
27    Directory {
28        key: PreviewKey,
29        path: PathBuf,
30    },
31    File {
32        key: PreviewKey,
33        path: PathBuf,
34    },
35    Metadata {
36        key: PreviewKey,
37        title: String,
38    },
39}
40
41impl PreviewRequest {
42    #[must_use]
43    pub fn key(&self) -> &PreviewKey {
44        match self {
45            Self::SessionSummary { key, .. }
46            | Self::Directory { key, .. }
47            | Self::File { key, .. }
48            | Self::Metadata { key, .. } => key,
49        }
50    }
51
52    #[must_use]
53    pub fn kind(&self) -> PreviewKind {
54        match self {
55            Self::SessionSummary { .. } => PreviewKind::SessionSummary,
56            Self::Directory { .. } => PreviewKind::Directory,
57            Self::File { .. } => PreviewKind::File,
58            Self::Metadata { .. } => PreviewKind::Metadata,
59        }
60    }
61}
62
63#[derive(Debug, Clone, PartialEq, Eq)]
64pub struct PreviewContent {
65    pub title: String,
66    pub body: Vec<String>,
67    pub truncated: bool,
68}
69
70impl PreviewContent {
71    #[must_use]
72    pub fn from_text(title: impl Into<String>, text: impl AsRef<str>, max_lines: usize) -> Self {
73        let mut body = text
74            .as_ref()
75            .lines()
76            .take(max_lines)
77            .map(ToOwned::to_owned)
78            .collect::<Vec<_>>();
79        let truncated = text.as_ref().lines().count() > max_lines;
80
81        if body.is_empty() {
82            body.push(String::new());
83        }
84
85        Self {
86            title: title.into(),
87            body,
88            truncated,
89        }
90    }
91
92    #[must_use]
93    pub fn from_text_tail(
94        title: impl Into<String>,
95        text: impl AsRef<str>,
96        max_lines: usize,
97    ) -> Self {
98        let lines = text
99            .as_ref()
100            .lines()
101            .map(ToOwned::to_owned)
102            .collect::<Vec<_>>();
103        let truncated = lines.len() > max_lines;
104        let start = lines.len().saturating_sub(max_lines);
105        let mut body = lines.into_iter().skip(start).collect::<Vec<_>>();
106
107        if body.is_empty() {
108            body.push(String::new());
109        }
110
111        Self {
112            title: title.into(),
113            body,
114            truncated,
115        }
116    }
117}
118
119#[must_use]
120pub fn preview_request_for_candidate(candidate: &Candidate) -> PreviewRequest {
121    match &candidate.metadata {
122        CandidateMetadata::Session(metadata) => PreviewRequest::SessionSummary {
123            key: candidate.preview_key.clone(),
124            session_name: metadata.session_name.clone(),
125        },
126        CandidateMetadata::Directory(metadata) => PreviewRequest::Directory {
127            key: candidate.preview_key.clone(),
128            path: metadata.full_path.clone(),
129        },
130        CandidateMetadata::Window(metadata) => PreviewRequest::Metadata {
131            key: candidate.preview_key.clone(),
132            title: format!("{}:{}", metadata.session_name, metadata.index),
133        },
134        CandidateMetadata::Project(metadata) => PreviewRequest::Directory {
135            key: candidate.preview_key.clone(),
136            path: metadata.root.clone(),
137        },
138        CandidateMetadata::Worktree(metadata) => PreviewRequest::Directory {
139            key: candidate.preview_key.clone(),
140            path: metadata.full_path.clone(),
141        },
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use std::path::PathBuf;
148
149    use crate::{
150        Candidate, PreviewKey, PreviewRequest, WorktreeMetadata, preview_request_for_candidate,
151    };
152
153    #[test]
154    fn maps_worktree_candidates_to_directory_previews_using_full_path() {
155        let full_path = PathBuf::from("/tmp/demo-worktree");
156        let candidate = Candidate::worktree(WorktreeMetadata {
157            full_path: full_path.clone(),
158            display_path: "~/demo-worktree".to_string(),
159            branch: Some("feature/demo".to_string()),
160        });
161
162        let request = preview_request_for_candidate(&candidate);
163
164        assert_eq!(
165            request,
166            PreviewRequest::Directory {
167                key: PreviewKey::Directory(full_path.clone()),
168                path: full_path,
169            }
170        );
171    }
172}