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
//! `git cat-file` — provide content or type/size information for repository objects.
use crate::command::{CommandExecutor, GitCommand};
use crate::error::{Error, Result};
use async_trait::async_trait;
/// Mode of operation for `cat-file`.
#[derive(Debug, Clone, Copy)]
pub enum CatFileMode {
/// `-t`: print the object's type.
Type,
/// `-s`: print the object's size.
Size,
/// `-e`: exit 0 if object exists, non-zero otherwise.
Exists,
/// `-p`: pretty-print the object's contents.
PrettyPrint,
}
/// Builder for `git cat-file`.
#[derive(Debug, Clone)]
pub struct CatFileCommand {
/// Shared executor.
pub executor: CommandExecutor,
/// Operation mode.
pub mode: CatFileMode,
/// Object to inspect.
pub object: String,
}
impl CatFileCommand {
/// Create a `cat-file -p <object>` command.
pub fn pretty_print(object: impl Into<String>) -> Self {
Self {
executor: CommandExecutor::default(),
mode: CatFileMode::PrettyPrint,
object: object.into(),
}
}
/// Create a `cat-file -t <object>` command.
pub fn object_type(object: impl Into<String>) -> Self {
Self {
executor: CommandExecutor::default(),
mode: CatFileMode::Type,
object: object.into(),
}
}
/// Create a `cat-file -s <object>` command.
pub fn size(object: impl Into<String>) -> Self {
Self {
executor: CommandExecutor::default(),
mode: CatFileMode::Size,
object: object.into(),
}
}
/// Create a `cat-file -e <object>` command.
pub fn exists(object: impl Into<String>) -> Self {
Self {
executor: CommandExecutor::default(),
mode: CatFileMode::Exists,
object: object.into(),
}
}
/// Run the command and return stdout as raw, untrimmed bytes.
///
/// Prefer this over [`execute`](GitCommand::execute) when the object may be
/// binary (typically `pretty_print` on a blob): `execute` decodes stdout
/// lossily as UTF-8 and trims trailing whitespace, either of which corrupts
/// binary content.
pub async fn execute_bytes(&self) -> Result<Vec<u8>> {
if self.object.is_empty() {
return Err(Error::invalid_config(
"cat-file requires a non-empty object",
));
}
let out = self.execute_raw().await?;
Ok(out.stdout)
}
}
#[async_trait]
impl GitCommand for CatFileCommand {
/// Trimmed, lossily-decoded stdout. For `Exists` mode, success is reported
/// via `Ok(String::new())`; a missing object returns
/// [`Error::CommandFailed`]. For binary blobs use
/// [`execute_bytes`](CatFileCommand::execute_bytes) instead.
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 flag = match self.mode {
CatFileMode::Type => "-t",
CatFileMode::Size => "-s",
CatFileMode::Exists => "-e",
CatFileMode::PrettyPrint => "-p",
};
vec!["cat-file".into(), flag.into(), self.object.clone()]
}
async fn execute(&self) -> Result<String> {
if self.object.is_empty() {
return Err(Error::invalid_config(
"cat-file requires a non-empty object",
));
}
let out = self.execute_raw().await?;
Ok(out.stdout_trimmed())
}
}