1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use std::env;
use std::path::PathBuf;
use std::io; // Import io for error handling
#[derive(Debug, Clone)]
pub struct DirStack {
stack: Vec<PathBuf>, // Use PathBuf for better path handling
}
impl DirStack {
/// Creates a new DirStack, initializing it with the current working directory.
pub fn new(path: Option<&PathBuf>) -> io::Result<Self> {
let initial_dir = match path {
Some(p) => p.clone(),
None => env::current_dir().map_err(|e| {
io::Error::new(
e.kind(),
format!("Failed to get current directory: {}", e),
)
})?,
};
// Ensure the initial directory is canonical and exists
let canonical_path = initial_dir.canonicalize().map_err(
|e| {
io::Error::new(
e.kind(),
format!("Failed to find or access directory '{}': {}", initial_dir.display(), e),
)
},
)?;
if !canonical_path.is_dir() {
return Err(io::Error::new(
io::ErrorKind::NotADirectory,
format!("Path '{}' is not a directory", canonical_path.display()),
));
}
env::set_current_dir(&canonical_path).map_err(|e| {
io::Error::new(
e.kind(),
format!("Failed to set current directory to '{}': {}", canonical_path.display(), e),
)
})? ; // Set the current working directory
Ok(Self {
stack: vec![canonical_path], // Initialize stack with the canonical path
})
}
/// Pushes a new directory onto the stack and changes the current working directory.
/// The `dir` path is interpreted relative to the *current* directory on the stack.
pub fn push(&mut self, dir: &str) -> io::Result<()> {
let target_dir = if let Some(current) = self.stack.last() {
// Resolve the new path relative to the current top of the stack
current.join(dir)
} else {
// Should not happen if initialized correctly, but handle defensively
PathBuf::from(dir)
};
// Attempt to canonicalize to get an absolute path and verify existence
let canonical_path = match target_dir.canonicalize() {
Ok(p) => p,
Err(e) => {
// Provide more context on error
return Err(io::Error::new(
e.kind(),
format!("(Push)Failed to find or access directory '{}': {}", target_dir.display(), e),
));
}
};
// Check if it's actually a directory
if !canonical_path.is_dir() {
return Err(io::Error::new(
io::ErrorKind::NotADirectory,
format!("(Push)Path '{}' is not a directory", canonical_path.display()),
));
}
env::set_current_dir(&canonical_path).map_err(
|e| io::Error::new(
e.kind(),
format!("(Push)Failed to set current directory to '{}': {}", canonical_path.display(), e),
)
)?; // Change the working directory
self.stack.push(canonical_path); // Push the canonical path onto the stack
Ok(())
}
/// Pops the current directory from the stack and changes the working directory
/// back to the previous one. Returns the directory popped, or None if only
/// the initial directory remains.
pub fn pop(&mut self) -> io::Result<Option<PathBuf>> {
if self.stack.len() <= 1 {
// Cannot pop the initial directory
return Ok(None);
}
let popped_dir = self.stack.pop(); // Remove the top directory
if let Some(previous_dir) = self.stack.last() {
env::set_current_dir(previous_dir).map_err(|e| {
io::Error::new(
e.kind(),
format!("(Pop)Failed to set current directory to '{}': {}", previous_dir.display(), e),
)
})?; // Change the working directory to the new top of the stack
}
// If stack somehow became empty (shouldn't happen with the check above),
// we don't change dir, but still return the popped one.
Ok(popped_dir)
}
}
// Optional: Add a default implementation
impl Default for DirStack {
fn default() -> Self {
// In Default, we might panic on error or return a basic state
// For simplicity here, we'll use "." if getting current_dir fails.
let initial_dir = env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
Self {
stack: vec![initial_dir],
}
}
}