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>) -> &mut 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    pub fn include_untracked(&mut self) -> &mut Self {
72        if let StashAction::Push {
73            include_untracked, ..
74        } = &mut self.action
75        {
76            *include_untracked = true;
77        }
78        self
79    }
80
81    /// Keep index intact.
82    pub fn keep_index(&mut self) -> &mut Self {
83        if let StashAction::Push { keep_index, .. } = &mut self.action {
84            *keep_index = true;
85        }
86        self
87    }
88
89    /// `stash pop`.
90    #[must_use]
91    pub fn pop(stash: Option<String>) -> Self {
92        Self {
93            executor: CommandExecutor::default(),
94            action: StashAction::Pop(stash),
95        }
96    }
97
98    /// `stash apply`.
99    #[must_use]
100    pub fn apply(stash: Option<String>) -> Self {
101        Self {
102            executor: CommandExecutor::default(),
103            action: StashAction::Apply(stash),
104        }
105    }
106
107    /// `stash drop`.
108    #[must_use]
109    pub fn drop_stash(stash: Option<String>) -> Self {
110        Self {
111            executor: CommandExecutor::default(),
112            action: StashAction::Drop(stash),
113        }
114    }
115
116    /// `stash list`.
117    #[must_use]
118    pub fn list() -> Self {
119        Self {
120            executor: CommandExecutor::default(),
121            action: StashAction::List,
122        }
123    }
124
125    /// `stash show`.
126    #[must_use]
127    pub fn show(stash: Option<String>) -> Self {
128        Self {
129            executor: CommandExecutor::default(),
130            action: StashAction::Show(stash),
131        }
132    }
133
134    /// `stash clear`.
135    #[must_use]
136    pub fn clear() -> Self {
137        Self {
138            executor: CommandExecutor::default(),
139            action: StashAction::Clear,
140        }
141    }
142}
143
144#[async_trait]
145impl GitCommand for StashCommand {
146    type Output = CommandOutput;
147    fn get_executor(&self) -> &CommandExecutor {
148        &self.executor
149    }
150    fn get_executor_mut(&mut self) -> &mut CommandExecutor {
151        &mut self.executor
152    }
153    fn build_command_args(&self) -> Vec<String> {
154        let mut args = vec!["stash".to_string()];
155        match &self.action {
156            StashAction::Push {
157                message,
158                include_untracked,
159                keep_index,
160            } => {
161                args.push("push".into());
162                if *include_untracked {
163                    args.push("--include-untracked".into());
164                }
165                if *keep_index {
166                    args.push("--keep-index".into());
167                }
168                if let Some(m) = message {
169                    args.push("-m".into());
170                    args.push(m.clone());
171                }
172            }
173            StashAction::Pop(s) => {
174                args.push("pop".into());
175                if let Some(s) = s {
176                    args.push(s.clone());
177                }
178            }
179            StashAction::Apply(s) => {
180                args.push("apply".into());
181                if let Some(s) = s {
182                    args.push(s.clone());
183                }
184            }
185            StashAction::Drop(s) => {
186                args.push("drop".into());
187                if let Some(s) = s {
188                    args.push(s.clone());
189                }
190            }
191            StashAction::List => args.push("list".into()),
192            StashAction::Show(s) => {
193                args.push("show".into());
194                if let Some(s) = s {
195                    args.push(s.clone());
196                }
197            }
198            StashAction::Clear => args.push("clear".into()),
199        }
200        args
201    }
202    async fn execute(&self) -> Result<CommandOutput> {
203        self.execute_raw().await
204    }
205}