Skip to main content

git_spawn/command/
stash.rs

1//! `git stash` — stash the changes in a dirty working directory away.
2
3use crate::command::{CommandExecutor, CommandOutput, GitCommand};
4use crate::error::Result;
5use async_trait::async_trait;
6
7/// Actions supported by `git stash`.
8#[derive(Debug, Clone)]
9pub enum StashAction {
10    /// `git stash push [-m <msg>]`.
11    Push {
12        /// Optional message.
13        message: Option<String>,
14        /// Include untracked files.
15        include_untracked: bool,
16        /// Keep the index intact.
17        keep_index: bool,
18    },
19    /// `git stash pop [stash@{n}]`.
20    Pop(Option<String>),
21    /// `git stash apply [stash@{n}]`.
22    Apply(Option<String>),
23    /// `git stash drop [stash@{n}]`.
24    Drop(Option<String>),
25    /// `git stash list`.
26    List,
27    /// `git stash show [stash@{n}]`.
28    Show(Option<String>),
29    /// `git stash clear`.
30    Clear,
31}
32
33/// Builder for `git stash`.
34#[derive(Debug, Clone)]
35pub struct StashCommand {
36    /// Shared executor.
37    pub executor: CommandExecutor,
38    /// Action.
39    pub action: StashAction,
40}
41
42impl Default for StashCommand {
43    fn default() -> Self {
44        Self {
45            executor: CommandExecutor::default(),
46            action: StashAction::Push {
47                message: None,
48                include_untracked: false,
49                keep_index: false,
50            },
51        }
52    }
53}
54
55impl StashCommand {
56    /// `stash push`.
57    #[must_use]
58    pub fn push() -> Self {
59        Self::default()
60    }
61
62    /// Set push message.
63    pub fn message(mut self, m: impl Into<String>) -> Self {
64        if let StashAction::Push { message, .. } = &mut self.action {
65            *message = Some(m.into());
66        }
67        self
68    }
69
70    /// Include untracked files.
71    #[must_use]
72    pub fn include_untracked(mut self) -> Self {
73        if let StashAction::Push {
74            include_untracked, ..
75        } = &mut self.action
76        {
77            *include_untracked = true;
78        }
79        self
80    }
81
82    /// Keep index intact.
83    #[must_use]
84    pub fn keep_index(mut self) -> Self {
85        if let StashAction::Push { keep_index, .. } = &mut self.action {
86            *keep_index = true;
87        }
88        self
89    }
90
91    /// `stash pop`.
92    #[must_use]
93    pub fn pop(stash: Option<String>) -> Self {
94        Self {
95            executor: CommandExecutor::default(),
96            action: StashAction::Pop(stash),
97        }
98    }
99
100    /// `stash apply`.
101    #[must_use]
102    pub fn apply(stash: Option<String>) -> Self {
103        Self {
104            executor: CommandExecutor::default(),
105            action: StashAction::Apply(stash),
106        }
107    }
108
109    /// `stash drop`.
110    #[must_use]
111    pub fn drop_stash(stash: Option<String>) -> Self {
112        Self {
113            executor: CommandExecutor::default(),
114            action: StashAction::Drop(stash),
115        }
116    }
117
118    /// `stash list`.
119    #[must_use]
120    pub fn list() -> Self {
121        Self {
122            executor: CommandExecutor::default(),
123            action: StashAction::List,
124        }
125    }
126
127    /// `stash show`.
128    #[must_use]
129    pub fn show(stash: Option<String>) -> Self {
130        Self {
131            executor: CommandExecutor::default(),
132            action: StashAction::Show(stash),
133        }
134    }
135
136    /// `stash clear`.
137    #[must_use]
138    pub fn clear() -> Self {
139        Self {
140            executor: CommandExecutor::default(),
141            action: StashAction::Clear,
142        }
143    }
144}
145
146#[async_trait]
147impl GitCommand for StashCommand {
148    type Output = CommandOutput;
149    fn get_executor(&self) -> &CommandExecutor {
150        &self.executor
151    }
152    fn get_executor_mut(&mut self) -> &mut CommandExecutor {
153        &mut self.executor
154    }
155    fn build_command_args(&self) -> Vec<String> {
156        let mut args = vec!["stash".to_string()];
157        match &self.action {
158            StashAction::Push {
159                message,
160                include_untracked,
161                keep_index,
162            } => {
163                args.push("push".into());
164                if *include_untracked {
165                    args.push("--include-untracked".into());
166                }
167                if *keep_index {
168                    args.push("--keep-index".into());
169                }
170                if let Some(m) = message {
171                    args.push("-m".into());
172                    args.push(m.clone());
173                }
174            }
175            StashAction::Pop(s) => {
176                args.push("pop".into());
177                if let Some(s) = s {
178                    args.push(s.clone());
179                }
180            }
181            StashAction::Apply(s) => {
182                args.push("apply".into());
183                if let Some(s) = s {
184                    args.push(s.clone());
185                }
186            }
187            StashAction::Drop(s) => {
188                args.push("drop".into());
189                if let Some(s) = s {
190                    args.push(s.clone());
191                }
192            }
193            StashAction::List => args.push("list".into()),
194            StashAction::Show(s) => {
195                args.push("show".into());
196                if let Some(s) = s {
197                    args.push(s.clone());
198                }
199            }
200            StashAction::Clear => args.push("clear".into()),
201        }
202        args
203    }
204    async fn execute(&self) -> Result<CommandOutput> {
205        self.execute_raw().await
206    }
207}