Skip to main content

git_spawn/command/
hash_object.rs

1//! `git hash-object` — compute object ID and optionally create a blob from a file.
2
3use crate::command::{CommandExecutor, GitCommand};
4use crate::error::{Error, Result};
5use async_trait::async_trait;
6use std::path::PathBuf;
7
8/// Builder for `git hash-object`.
9#[derive(Debug, Clone, Default)]
10pub struct HashObjectCommand {
11    /// Shared executor.
12    pub executor: CommandExecutor,
13    /// Path(s) to hash.
14    pub paths: Vec<PathBuf>,
15    /// `-w`: also write the object into the object database.
16    pub write: bool,
17    /// `--stdin`: read from stdin. (Currently unsupported in this wrapper; use
18    /// `path()` on a file containing the desired bytes.)
19    pub stdin: bool,
20    /// `-t <type>`: override the object type.
21    pub object_type: Option<String>,
22}
23
24impl HashObjectCommand {
25    /// New command.
26    #[must_use]
27    pub fn new() -> Self {
28        Self::default()
29    }
30
31    /// Hash the file at `path`.
32    pub fn path(&mut self, p: impl Into<PathBuf>) -> &mut Self {
33        self.paths.push(p.into());
34        self
35    }
36
37    /// Also write the resulting object into `.git/objects`.
38    pub fn write(&mut self) -> &mut Self {
39        self.write = true;
40        self
41    }
42
43    /// Override the object type (e.g. `"tree"`, `"commit"`, `"tag"`).
44    pub fn object_type(&mut self, t: impl Into<String>) -> &mut Self {
45        self.object_type = Some(t.into());
46        self
47    }
48}
49
50#[async_trait]
51impl GitCommand for HashObjectCommand {
52    /// The computed SHA(s). When multiple paths are provided, one SHA per line.
53    type Output = String;
54
55    fn get_executor(&self) -> &CommandExecutor {
56        &self.executor
57    }
58
59    fn get_executor_mut(&mut self) -> &mut CommandExecutor {
60        &mut self.executor
61    }
62
63    fn build_command_args(&self) -> Vec<String> {
64        let mut args = vec!["hash-object".to_string()];
65        if self.write {
66            args.push("-w".into());
67        }
68        if let Some(t) = &self.object_type {
69            args.push("-t".into());
70            args.push(t.clone());
71        }
72        if self.stdin {
73            args.push("--stdin".into());
74        }
75        args.extend(self.paths.iter().map(|p| p.display().to_string()));
76        args
77    }
78
79    async fn execute(&self) -> Result<String> {
80        if self.paths.is_empty() && !self.stdin {
81            return Err(Error::invalid_config(
82                "hash-object requires at least one path",
83            ));
84        }
85        let out = self.execute_raw().await?;
86        Ok(out.stdout_trimmed().to_string())
87    }
88}