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
use crate::cli::repo::fork::RepoForkArgs;
use crate::client::BergClient;
use crate::render::spinner::{spin_and_try_every_second_for, spin_until_ready};
pub async fn fork_repo(args: RepoForkArgs, client: &BergClient) -> anyhow::Result<()> {
let (ownername, reponame) = parse_owner_and_repo(args.owner_and_repo)?;
let ssh_url = spin_until_ready(start_fork_repo(
client,
ownername.as_str(),
reponame.as_str(),
))
.await?;
ask_confirm_clone(reponame.as_str())?;
start_clone_repo(ssh_url)?;
Ok(())
}
fn parse_owner_and_repo(owner_and_repo: String) -> anyhow::Result<(String, String)> {
owner_and_repo
.split_once('/')
.map(|(owner, repo)| (owner.to_owned(), repo.to_owned()))
.ok_or_else(|| {
anyhow::anyhow!(
"Please provide the repository you want to fork in the format OWNER/REPO."
)
})
}
async fn start_fork_repo(
client: &BergClient,
ownername: &str,
reponame: &str,
) -> anyhow::Result<String> {
let ssh_url_original = get_ssh_url(client, ownername, reponame).await?;
tracing::error!("Fork url: {ssh_url_original}");
client.fork_repo(ownername, reponame).await?;
tracing::debug!("Forked successfully.");
let user = client.get_user_info().await?;
tracing::debug!("User: {user:?}");
let new_url =
spin_and_try_every_second_for(|| get_ssh_url(client, user.username.as_str(), reponame), 10)
.await?;
tracing::debug!("Forked Repo SSH URL: {new_url:?}");
Ok(new_url)
}
async fn get_ssh_url(
client: &BergClient,
ownername: &str,
reponame: &str,
) -> anyhow::Result<String> {
let owner_repos = client.get_user_or_org_repos(ownername.to_owned()).await?;
owner_repos
.iter()
.find(|repo| repo.name == reponame)
.ok_or_else(|| anyhow::anyhow!("User {ownername} doesn't own the repo {reponame}."))
.map(|repo| repo.ssh_url.to_owned())
}
fn ask_confirm_clone(reponame: &str) -> anyhow::Result<()> {
let current_path = std::env::current_dir()?;
inquire::Confirm::new(
format!("Do you really to fork {reponame} into the directory {current_path:?}").as_str(),
)
.prompt()
.map_err(anyhow::Error::from)
.and_then(|confirmed| {
confirmed
.then_some(())
.ok_or_else(|| anyhow::anyhow!("Abort cloning the repository."))
})
}
fn start_clone_repo(ssh_url: String) -> anyhow::Result<()> {
let mut cmd = std::process::Command::new("git");
cmd.arg("clone").arg(ssh_url);
tracing::debug!("cmd: {cmd:?}");
cmd.stdout(std::process::Stdio::inherit()).spawn()?;
Ok(())
}