ironflow_ops_git/
fetch.rs1use std::path::PathBuf;
4
5use async_trait::async_trait;
6use git2::Repository;
7use ironflow_core::error::OperationError;
8use ironflow_core::operation::{Operation, OperationContext, TypedOperation};
9use serde::{Deserialize, Serialize};
10use serde_json::Value;
11
12use crate::helpers::{blocking, to_value};
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct FetchPushOutput {
16 pub remote: String,
17 pub refspecs: Vec<String>,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct RemotePruneOutput {
22 pub remote: String,
23 pub pruned: bool,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct RemoteDefaultBranchOutput {
28 pub remote: String,
29 pub default_branch: Option<String>,
30}
31
32pub struct FetchRemote {
44 repo_path: PathBuf,
45 remote_name: String,
46 refspecs: Vec<String>,
47}
48
49impl FetchRemote {
50 pub fn new(
52 repo_path: impl Into<PathBuf>,
53 remote_name: impl Into<String>,
54 refspecs: Vec<impl Into<String>>,
55 ) -> Self {
56 Self {
57 repo_path: repo_path.into(),
58 remote_name: remote_name.into(),
59 refspecs: refspecs.into_iter().map(Into::into).collect(),
60 }
61 }
62
63 pub async fn run(&self, _ctx: &OperationContext) -> Result<FetchPushOutput, OperationError> {
65 let repo_path = self.repo_path.clone();
66 let remote_name = self.remote_name.clone();
67 let refspecs = self.refspecs.clone();
68 blocking(move || {
69 let repo = Repository::open(&repo_path)?;
70 let mut remote = repo.find_remote(&remote_name)?;
71 let refs: Vec<&str> = refspecs.iter().map(String::as_str).collect();
72 remote.fetch(&refs, None, None)?;
73 Ok(FetchPushOutput {
74 remote: remote_name,
75 refspecs,
76 })
77 })
78 .await
79 }
80}
81
82#[async_trait]
83impl Operation for FetchRemote {
84 fn kind(&self) -> &str {
85 "git"
86 }
87 async fn execute(&self, ctx: &OperationContext) -> Result<Value, OperationError> {
88 to_value(&self.run(ctx).await?)
89 }
90 fn input(&self) -> Option<Value> {
91 Some(serde_json::json!({ "repo_path": self.repo_path, "remote": self.remote_name }))
92 }
93}
94
95impl TypedOperation for FetchRemote {
96 type Output = FetchPushOutput;
97}
98
99pub struct PushRemote {
111 repo_path: PathBuf,
112 remote_name: String,
113 refspecs: Vec<String>,
114}
115
116impl PushRemote {
117 pub fn new(
119 repo_path: impl Into<PathBuf>,
120 remote_name: impl Into<String>,
121 refspecs: Vec<impl Into<String>>,
122 ) -> Self {
123 Self {
124 repo_path: repo_path.into(),
125 remote_name: remote_name.into(),
126 refspecs: refspecs.into_iter().map(Into::into).collect(),
127 }
128 }
129
130 pub async fn run(&self, _ctx: &OperationContext) -> Result<FetchPushOutput, OperationError> {
132 let repo_path = self.repo_path.clone();
133 let remote_name = self.remote_name.clone();
134 let refspecs = self.refspecs.clone();
135 blocking(move || {
136 let repo = Repository::open(&repo_path)?;
137 let mut remote = repo.find_remote(&remote_name)?;
138 let refs: Vec<&str> = refspecs.iter().map(String::as_str).collect();
139 remote.push(&refs, None)?;
140 Ok(FetchPushOutput {
141 remote: remote_name,
142 refspecs,
143 })
144 })
145 .await
146 }
147}
148
149#[async_trait]
150impl Operation for PushRemote {
151 fn kind(&self) -> &str {
152 "git"
153 }
154 async fn execute(&self, ctx: &OperationContext) -> Result<Value, OperationError> {
155 to_value(&self.run(ctx).await?)
156 }
157 fn input(&self) -> Option<Value> {
158 Some(serde_json::json!({ "repo_path": self.repo_path, "remote": self.remote_name }))
159 }
160}
161
162impl TypedOperation for PushRemote {
163 type Output = FetchPushOutput;
164}
165
166pub struct RemotePrune {
178 repo_path: PathBuf,
179 remote_name: String,
180}
181
182impl RemotePrune {
183 pub fn new(repo_path: impl Into<PathBuf>, remote_name: impl Into<String>) -> Self {
185 Self {
186 repo_path: repo_path.into(),
187 remote_name: remote_name.into(),
188 }
189 }
190
191 pub async fn run(&self, _ctx: &OperationContext) -> Result<RemotePruneOutput, OperationError> {
193 let repo_path = self.repo_path.clone();
194 let remote_name = self.remote_name.clone();
195 blocking(move || {
196 let repo = Repository::open(&repo_path)?;
197 let mut remote = repo.find_remote(&remote_name)?;
198 remote.prune(None)?;
199 Ok(RemotePruneOutput {
200 remote: remote_name,
201 pruned: true,
202 })
203 })
204 .await
205 }
206}
207
208#[async_trait]
209impl Operation for RemotePrune {
210 fn kind(&self) -> &str {
211 "git"
212 }
213 async fn execute(&self, ctx: &OperationContext) -> Result<Value, OperationError> {
214 to_value(&self.run(ctx).await?)
215 }
216 fn input(&self) -> Option<Value> {
217 Some(serde_json::json!({ "repo_path": self.repo_path, "remote": self.remote_name }))
218 }
219}
220
221impl TypedOperation for RemotePrune {
222 type Output = RemotePruneOutput;
223}
224
225pub struct RemoteDefaultBranch {
237 repo_path: PathBuf,
238 remote_name: String,
239}
240
241impl RemoteDefaultBranch {
242 pub fn new(repo_path: impl Into<PathBuf>, remote_name: impl Into<String>) -> Self {
244 Self {
245 repo_path: repo_path.into(),
246 remote_name: remote_name.into(),
247 }
248 }
249
250 pub async fn run(
252 &self,
253 _ctx: &OperationContext,
254 ) -> Result<RemoteDefaultBranchOutput, OperationError> {
255 let repo_path = self.repo_path.clone();
256 let remote_name = self.remote_name.clone();
257 blocking(move || {
258 let repo = Repository::open(&repo_path)?;
259 let remote = repo.find_remote(&remote_name)?;
260 let default = remote.default_branch()?;
261 let name = default.as_str().map(String::from);
262 Ok(RemoteDefaultBranchOutput {
263 remote: remote_name,
264 default_branch: name,
265 })
266 })
267 .await
268 }
269}
270
271#[async_trait]
272impl Operation for RemoteDefaultBranch {
273 fn kind(&self) -> &str {
274 "git"
275 }
276 async fn execute(&self, ctx: &OperationContext) -> Result<Value, OperationError> {
277 to_value(&self.run(ctx).await?)
278 }
279 fn input(&self) -> Option<Value> {
280 Some(serde_json::json!({ "repo_path": self.repo_path, "remote": self.remote_name }))
281 }
282}
283
284impl TypedOperation for RemoteDefaultBranch {
285 type Output = RemoteDefaultBranchOutput;
286}