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!(get_missing_session_cwd_issue(None, Path::new("/whatever"), Path::new("/cwd")).is_none());
104 }
105
106 #[test]
107 fn existing_dir_is_ok() {
108 let dir = std::env::temp_dir();
109 assert!(get_missing_session_cwd_issue(Some(Path::new("/s.jsonl")), &dir, &dir).is_none());
110 }
111
112 #[test]
113 fn empty_stored_cwd_is_ok() {
114 assert!(get_missing_session_cwd_issue(Some(Path::new("/s.jsonl")), Path::new(""), Path::new("/cwd")).is_none());
115 }
116
117 #[test]
118 fn missing_dir_reports_issue() {
119 let missing = PathBuf::from("/definitely/not/here/xyzzy-1234");
120 let issue = get_missing_session_cwd_issue(Some(Path::new("/s.jsonl")), &missing, Path::new("/cwd"))
121 .expect("issue");
122 assert_eq!(issue.session_cwd, missing);
123 assert!(format_missing_session_cwd_error(&issue).contains("does not exist"));
124 assert!(format_missing_session_cwd_prompt(&issue).contains("continue in current cwd"));
125 assert!(assert_session_cwd_exists(Some(Path::new("/s.jsonl")), &missing, Path::new("/cwd")).is_err());
126 }
127}