Skip to content

Commit b19bc2d

Browse files
committed
refactor: more type safety with newtypes
1 parent 6558def commit b19bc2d

File tree

8 files changed

+189
-91
lines changed

8 files changed

+189
-91
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ once_cell = "1.17"
4343
documented = "0.9"
4444
log = "0.4"
4545
env_logger = "0.11"
46-
nutype = "0.6"
46+
nutype = { version = "0.6", features = ["serde"] }
4747
clap = { version = "4.5.39", features = [
4848
"derive",
4949
"wrap_help",

src/cli.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ use clap::{
77
builder::styling::{AnsiColor, Effects},
88
};
99

10-
use crate::{commands, config::Commit, config::Remote};
10+
use crate::{
11+
commands,
12+
config::{BranchName, Commit, Remote},
13+
};
1114

1215
/// A tool which makes it easy to declaratively manage personal forks by automatically merging pull requests
1316
#[derive(Parser, Debug)]
@@ -51,7 +54,7 @@ pub enum Command {
5154
remote: Option<Remote>,
5255
/// Choose a custom branch name for the fetched repo
5356
#[arg(short, long)]
54-
branch: Option<String>,
57+
branch: Option<BranchName>,
5558
/// When fetching this PR, reset to this commit
5659
#[arg(short = 'C', long)]
5760
commit: Option<Commit>,
@@ -144,7 +147,7 @@ mod test {
144147
Remote {
145148
owner: "helix-editor".to_string(),
146149
repo: "helix".to_string(),
147-
branch: "main".to_string(),
150+
branch: BranchName::try_new("main").unwrap(),
148151
commit: None
149152
}
150153
);
@@ -155,7 +158,7 @@ mod test {
155158
Remote {
156159
owner: "helix-editor".to_string(),
157160
repo: "helix".to_string(),
158-
branch: "master".to_string(),
161+
branch: BranchName::try_new("master").unwrap(),
159162
commit: Some(Commit::try_new("1a2b3c").unwrap())
160163
}
161164
);

src/commands/branch_fetch.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub async fn branch_fetch(
1919
remote.owner,
2020
remote.repo,
2121
info.branch.upstream_branch_name,
22-
info.branch.local_branch_name.bright_cyan(),
22+
info.branch.local_branch_name.as_ref().bright_cyan(),
2323
commit
2424
.map(|commit_hash| { format!(", at commit {}", commit_hash.as_ref().bright_yellow()) })
2525
.unwrap_or_default()
@@ -29,7 +29,7 @@ pub async fn branch_fetch(
2929
let _ = git(["remote", "remove", &info.remote.local_remote_alias]);
3030

3131
if checkout {
32-
git(["checkout", &info.branch.local_branch_name]).map_err(|err| {
32+
git(["checkout", info.branch.local_branch_name.as_ref()]).map_err(|err| {
3333
anyhow!(
3434
"failed to check out branch {}:\n{err}",
3535
info.branch.local_branch_name

src/commands/pr_fetch.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use anyhow::{Context as _, anyhow};
44
use colored::Colorize as _;
55

6-
use crate::config::{Commit, Remote};
6+
use crate::config::{BranchName, Commit, Remote};
77
use crate::git::{fetch_pull_request, git};
88

99
/// Fetch the given `pr` of `remote` at `commit` and store it in local `branch`
@@ -12,7 +12,7 @@ use crate::git::{fetch_pull_request, git};
1212
pub async fn pr_fetch(
1313
pr: u32,
1414
remote: Option<Remote>,
15-
branch: Option<String>,
15+
branch: Option<BranchName>,
1616
commit: Option<Commit>,
1717
checkout: bool,
1818
) -> anyhow::Result<()> {
@@ -35,7 +35,7 @@ pub async fn pr_fetch(
3535
Ok(Remote {
3636
owner: owner.to_string(),
3737
repo: repo.to_string(),
38-
branch: "main".to_string(),
38+
branch: BranchName::try_new("main").expect("`main` is a valid branch name"),
3939
commit: None,
4040
})
4141
} else {
@@ -48,8 +48,8 @@ pub async fn pr_fetch(
4848
match fetch_pull_request(
4949
&format!("{}/{}", remote.owner, remote.repo),
5050
// TODO: make fetch_pull_request accept a u32 instead
51-
&pr.to_string(),
52-
branch.as_deref(),
51+
pr,
52+
branch,
5353
commit.as_ref(),
5454
)
5555
.await
@@ -67,7 +67,7 @@ pub async fn pr_fetch(
6767
),
6868
&response.html_url
6969
),
70-
info.branch.local_branch_name.bright_cyan(),
70+
info.branch.local_branch_name.as_ref().bright_cyan(),
7171
commit
7272
.clone()
7373
.map(|commit_hash| {
@@ -80,7 +80,9 @@ pub async fn pr_fetch(
8080
let _ = git(["remote", "remove", &info.remote.local_remote_alias]);
8181

8282
if checkout {
83-
if let Err(cant_checkout) = git(["checkout", &info.branch.local_branch_name]) {
83+
if let Err(cant_checkout) =
84+
git(["checkout", info.branch.local_branch_name.as_ref()])
85+
{
8486
log::error!(
8587
"Could not check out branch {}:\n{cant_checkout}",
8688
info.branch.local_branch_name

src/commands/run.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! `run` subcommand
22
3-
use crate::config::{Config, Ref};
3+
use crate::config::{BranchName, Config, PullRequest};
44
use std::ffi::OsString;
55
use std::fs::{self, File};
66
use std::io::Write as _;
@@ -51,8 +51,8 @@ pub async fn run(yes: bool) -> anyhow::Result<()> {
5151
anyhow!("Could not parse `{root}/{CONFIG_FILE}` configuration file:\n{err}",)
5252
})?;
5353

54-
let Ref {
55-
item: remote_branch,
54+
let crate::config::Branch {
55+
name: remote_branch,
5656
commit,
5757
} = config.remote_branch;
5858

@@ -103,7 +103,8 @@ pub async fn run(yes: bool) -> anyhow::Result<()> {
103103
},
104104
branch: Branch {
105105
upstream_branch_name: remote_branch.clone(),
106-
local_branch_name: with_uuid(&remote_branch),
106+
local_branch_name: BranchName::try_new(with_uuid(remote_branch.as_ref()))
107+
.expect("adding UUID to branch name does not invalidate it"),
107108
},
108109
};
109110

@@ -128,14 +129,14 @@ pub async fn run(yes: bool) -> anyhow::Result<()> {
128129
// TODO: make this concurrent, see https://users.rust-lang.org/t/processing-subprocesses-concurrently/79638/3
129130
// Git cannot handle multiple threads executing commands in the same repository,
130131
// so we can't use threads, but we can run processes in the background
131-
for Ref {
132-
item: pull_request,
132+
for PullRequest {
133+
number: pull_request,
133134
commit,
134135
} in &config.pull_requests
135136
{
136137
// TODO: refactor this to not use such deep nesting
137138
let Ok((response, info)) =
138-
git::fetch_pull_request(&config.repo, pull_request, None, commit.as_ref())
139+
git::fetch_pull_request(&config.repo, *pull_request, None, commit.as_ref())
139140
.await
140141
.inspect_err(|err| {
141142
log::error!("failed to fetch branch from remote:\n{err}");
@@ -145,7 +146,7 @@ pub async fn run(yes: bool) -> anyhow::Result<()> {
145146
};
146147

147148
if let Err(err) =
148-
git::merge_pull_request(&info, pull_request, &response.title, &response.html_url)
149+
git::merge_pull_request(&info, *pull_request, &response.title, &response.html_url)
149150
{
150151
log::error!("failed to merge {pull_request}: {err}");
151152
continue;
@@ -157,7 +158,7 @@ pub async fn run(yes: bool) -> anyhow::Result<()> {
157158
&format!(
158159
"{}{}{}{}",
159160
"#".bright_blue(),
160-
pull_request.bright_blue(),
161+
pull_request.to_string().bright_blue(),
161162
" ".bright_blue(),
162163
&response.title.bright_blue().italic()
163164
),
@@ -188,7 +189,7 @@ pub async fn run(yes: bool) -> anyhow::Result<()> {
188189
"Merged branch {}/{}/{} {}",
189190
owner.bright_blue(),
190191
repo.bright_blue(),
191-
branch.bright_blue(),
192+
branch.as_ref().bright_blue(),
192193
remote
193194
.commit
194195
.as_ref()
@@ -274,7 +275,7 @@ pub async fn run(yes: bool) -> anyhow::Result<()> {
274275
if yes
275276
|| confirm_prompt!(
276277
"Overwrite branch {}? This is irreversible.",
277-
config.local_branch.cyan()
278+
config.local_branch.as_ref().cyan()
278279
)
279280
{
280281
// forcefully renames the branch we are currently on into the branch specified
@@ -285,12 +286,12 @@ pub async fn run(yes: bool) -> anyhow::Result<()> {
285286
"--move",
286287
"--force",
287288
&temporary_branch,
288-
&config.local_branch,
289+
config.local_branch.as_ref(),
289290
])?;
290291
if yes {
291292
log::info!(
292293
"Automatically overwrote branch {} since you supplied the {} flag",
293-
config.local_branch.cyan(),
294+
config.local_branch.as_ref().cyan(),
294295
"--yes".bright_magenta()
295296
);
296297
}
@@ -304,7 +305,7 @@ pub async fn run(yes: bool) -> anyhow::Result<()> {
304305
);
305306
log::info!(
306307
"You can still manually overwrite {} with:\n {overwrite_command}\n",
307-
config.local_branch.cyan(),
308+
config.local_branch.as_ref().cyan(),
308309
);
309310

310311
Ok(())

0 commit comments

Comments
 (0)