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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! `git symbolic-ref` — read or modify a symbolic ref (most commonly `HEAD`).
use crate::command::{CommandExecutor, GitCommand};
use crate::error::{Error, Result};
use async_trait::async_trait;
/// Actions supported by `git symbolic-ref`.
#[derive(Debug, Clone)]
pub enum SymbolicRefAction {
/// Read the target of `ref` (e.g. `HEAD` -> `refs/heads/main`).
Read {
/// Ref name to read.
name: String,
/// `--short` shows the short form (`main`).
short: bool,
},
/// Set `name` to point at `target`.
Set {
/// Ref name.
name: String,
/// Target ref.
target: String,
/// `-m <reason>` reflog message.
reason: Option<String>,
},
/// Delete the symbolic ref.
Delete {
/// Ref name.
name: String,
/// `-q` suppress errors.
quiet: bool,
},
}
/// Builder for `git symbolic-ref`.
#[derive(Debug, Clone)]
pub struct SymbolicRefCommand {
/// Shared executor.
pub executor: CommandExecutor,
/// Action.
pub action: SymbolicRefAction,
}
impl SymbolicRefCommand {
/// Read the target of `name` (e.g. `read("HEAD")`).
pub fn read(name: impl Into<String>) -> Self {
Self {
executor: CommandExecutor::default(),
action: SymbolicRefAction::Read {
name: name.into(),
short: false,
},
}
}
/// `--short` for a read (only applies when the action is [`read`](Self::read)).
#[must_use]
pub fn short(mut self) -> Self {
if let SymbolicRefAction::Read { short, .. } = &mut self.action {
*short = true;
}
self
}
/// Set `name` to point at `target`.
pub fn set(name: impl Into<String>, target: impl Into<String>) -> Self {
Self {
executor: CommandExecutor::default(),
action: SymbolicRefAction::Set {
name: name.into(),
target: target.into(),
reason: None,
},
}
}
/// Set the reflog reason (`-m`, only for [`set`](Self::set)).
#[must_use]
pub fn reason(mut self, r: impl Into<String>) -> Self {
if let SymbolicRefAction::Set { reason, .. } = &mut self.action {
*reason = Some(r.into());
}
self
}
/// Delete the symbolic ref.
pub fn delete(name: impl Into<String>) -> Self {
Self {
executor: CommandExecutor::default(),
action: SymbolicRefAction::Delete {
name: name.into(),
quiet: false,
},
}
}
/// `-q` (only for [`delete`](Self::delete)).
#[must_use]
pub fn quiet(mut self) -> Self {
if let SymbolicRefAction::Delete { quiet, .. } = &mut self.action {
*quiet = true;
}
self
}
}
#[async_trait]
impl GitCommand for SymbolicRefCommand {
/// Trimmed stdout — the resolved target for `read`, empty for `set` / `delete`.
type Output = String;
fn get_executor(&self) -> &CommandExecutor {
&self.executor
}
fn get_executor_mut(&mut self) -> &mut CommandExecutor {
&mut self.executor
}
fn build_command_args(&self) -> Vec<String> {
let mut args = vec!["symbolic-ref".to_string()];
match &self.action {
SymbolicRefAction::Read { name, short } => {
if *short {
args.push("--short".into());
}
args.push(name.clone());
}
SymbolicRefAction::Set {
name,
target,
reason,
} => {
if let Some(r) = reason {
args.push("-m".into());
args.push(r.clone());
}
args.push(name.clone());
args.push(target.clone());
}
SymbolicRefAction::Delete { name, quiet } => {
args.push("--delete".into());
if *quiet {
args.push("-q".into());
}
args.push(name.clone());
}
}
args
}
async fn execute(&self) -> Result<String> {
if let SymbolicRefAction::Read { name, .. } | SymbolicRefAction::Set { name, .. } =
&self.action
{
if name.is_empty() {
return Err(Error::invalid_config("symbolic-ref requires a ref name"));
}
}
let out = self.execute_raw().await?;
Ok(out.stdout_trimmed().to_string())
}
}