1use std::fmt;
10use std::path::{Path, PathBuf};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct SessionCwdIssue {
15 pub session_file: Option<PathBuf>,
17 pub session_cwd: PathBuf,
19 pub fallback_cwd: PathBuf,
21}
22
23pub fn get_missing_session_cwd_issue(
26 session_file: Option<&Path>,
27 session_cwd: &Path,
28 fallback_cwd: &Path,
29) -> Option<SessionCwdIssue> {
30 let session_file = session_file?;
31 if session_cwd.as_os_str().is_empty() {
32 return None;
33 }
34 if session_cwd.exists() {
35 return None;
36 }
37 Some(SessionCwdIssue {
38 session_file: Some(session_file.to_path_buf()),
39 session_cwd: session_cwd.to_path_buf(),
40 fallback_cwd: fallback_cwd.to_path_buf(),
41 })
42}
43
44pub fn format_missing_session_cwd_error(issue: &SessionCwdIssue) -> String {
46 let session_file = issue
47 .session_file
48 .as_ref()
49 .map(|p| format!("\nSession file: {}", p.display()))
50 .unwrap_or_default();
51 format!(
52 "Stored session working directory does not exist: {}{}\nCurrent working directory: {}",
53 issue.session_cwd.display(),
54 session_file,
55 issue.fallback_cwd.display()
56 )
57}
58
59pub fn format_missing_session_cwd_prompt(issue: &SessionCwdIssue) -> String {
62 format!(
63 "cwd from session file does not exist\n{}\n\ncontinue in current cwd\n{}",
64 issue.session_cwd.display(),
65 issue.fallback_cwd.display()
66 )
67}
68
69#[derive(Debug, Clone, PartialEq, Eq)]
72pub struct MissingSessionCwdError {
73 pub issue: SessionCwdIssue,
74}
75
76impl fmt::Display for MissingSessionCwdError {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 f.write_str(&format_missing_session_cwd_error(&self.issue))
79 }
80}
81
82impl std::error::Error for MissingSessionCwdError {}
83
84pub fn assert_session_cwd_exists(
86 session_file: Option<&Path>,
87 session_cwd: &Path,
88 fallback_cwd: &Path,
89) -> Result<(), MissingSessionCwdError> {
90 match get_missing_session_cwd_issue(session_file, session_cwd, fallback_cwd) {
91 Some(issue) => Err(MissingSessionCwdError { issue }),
92 None => Ok(()),
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99 use std::path::PathBuf;
100
101 #[test]
102 fn no_session_file_is_ok() {
103 assert!(
104 get_missing_session_cwd_issue(None, Path::new("/whatever"), Path::new("/cwd"))
105 .is_none()
106 );
107 }
108
109 #[test]
110 fn existing_dir_is_ok() {
111 let dir = std::env::temp_dir();
112 assert!(get_missing_session_cwd_issue(Some(Path::new("/s.jsonl")), &dir, &dir).is_none());
113 }
114
115 #[test]
116 fn empty_stored_cwd_is_ok() {
117 assert!(get_missing_session_cwd_issue(
118 Some(Path::new("/s.jsonl")),
119 Path::new(""),
120 Path::new("/cwd")
121 )
122 .is_none());
123 }
124
125 #[test]
126 fn missing_dir_reports_issue() {
127 let missing = PathBuf::from("/definitely/not/here/xyzzy-1234");
128 let issue =
129 get_missing_session_cwd_issue(Some(Path::new("/s.jsonl")), &missing, Path::new("/cwd"))
130 .expect("issue");
131 assert_eq!(issue.session_cwd, missing);
132 assert!(format_missing_session_cwd_error(&issue).contains("does not exist"));
133 assert!(format_missing_session_cwd_prompt(&issue).contains("continue in current cwd"));
134 assert!(assert_session_cwd_exists(
135 Some(Path::new("/s.jsonl")),
136 &missing,
137 Path::new("/cwd")
138 )
139 .is_err());
140 }
141}