Skip to content

Commit f0a0d2d

Browse files
committed
chore!: restructure gix-url parsing error variants
1 parent 2afad16 commit f0a0d2d

File tree

5 files changed

+81
-31
lines changed

5 files changed

+81
-31
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gix-url/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ home = "0.5.3"
3333
document-features = { version = "0.2.0", optional = true }
3434

3535
[dev-dependencies]
36+
assert_matches = "1.5.0"
3637
gix-testtools = { path = "../tests/tools" }
3738
libtest-mimic = "0.6.1"
3839

gix-url/src/parse/mod.rs

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,21 @@ use bstr::{BStr, BString, ByteSlice};
55
#[derive(Debug, thiserror::Error)]
66
#[allow(missing_docs)]
77
pub enum Error {
8-
#[error("Could not decode URL as UTF8")]
9-
Utf8(#[from] std::str::Utf8Error),
10-
#[error(transparent)]
11-
Url(#[from] url::ParseError),
12-
#[error("file URLs require an absolute or relative path to the repository")]
13-
MissingRepositoryPath,
14-
#[error("Relative URLs are not permitted: {url:?}")]
8+
#[error("{} \"{url}\" is not valid UTF-8", .kind.get_error_str())]
9+
Utf8 {
10+
url: BString,
11+
kind: UrlKind,
12+
source: std::str::Utf8Error,
13+
},
14+
#[error("{} {url:?} can not be parsed as valid URL", .kind.get_error_str())]
15+
Url {
16+
url: String,
17+
kind: UrlKind,
18+
source: url::ParseError,
19+
},
20+
#[error("{} \"{url}\" does not specify a path to a repository", .kind.get_error_str())]
21+
MissingRepositoryPath { url: BString, kind: UrlKind },
22+
#[error("URL {url:?} is relative which is not allowed in this context")]
1523
RelativeUrl { url: String },
1624
}
1725

@@ -21,6 +29,27 @@ impl From<Infallible> for Error {
2129
}
2230
}
2331

32+
///
33+
#[derive(Debug)]
34+
pub enum UrlKind {
35+
///
36+
Url,
37+
///
38+
Scp,
39+
///
40+
Local,
41+
}
42+
43+
impl UrlKind {
44+
fn get_error_str(&self) -> &'static str {
45+
match self {
46+
UrlKind::Url => "URL",
47+
UrlKind::Scp => "SCP-like target",
48+
UrlKind::Local => "local path",
49+
}
50+
}
51+
}
52+
2453
enum InputScheme {
2554
Url { protocol_end: usize },
2655
Scp { colon: usize },
@@ -61,7 +90,16 @@ pub fn parse(input: &BStr) -> Result<crate::Url, Error> {
6190
}
6291

6392
fn parse_url(input: &BStr) -> Result<crate::Url, Error> {
64-
let url = url::Url::parse(std::str::from_utf8(input)?)?;
93+
let input = std::str::from_utf8(input).map_err(|source| Error::Utf8 {
94+
url: input.to_owned(),
95+
kind: UrlKind::Url,
96+
source,
97+
})?;
98+
let url = url::Url::parse(input).map_err(|source| Error::Url {
99+
url: input.to_owned(),
100+
kind: UrlKind::Url,
101+
source,
102+
})?;
65103

66104
Ok(crate::Url {
67105
serialize_alternative_form: false,
@@ -79,7 +117,11 @@ fn parse_url(input: &BStr) -> Result<crate::Url, Error> {
79117
}
80118

81119
fn parse_scp(input: &BStr, colon: usize) -> Result<crate::Url, Error> {
82-
let input = std::str::from_utf8(input)?;
120+
let input = std::str::from_utf8(input).map_err(|source| Error::Utf8 {
121+
url: input.to_owned(),
122+
kind: UrlKind::Scp,
123+
source,
124+
})?;
83125

84126
// TODO: this incorrectly splits at IPv6 addresses, check for `[]` before splitting
85127
let (host, path) = input.split_at(colon);
@@ -89,7 +131,11 @@ fn parse_scp(input: &BStr, colon: usize) -> Result<crate::Url, Error> {
89131
// should never differ in any other way (ssh URLs should not contain a query or fragment part).
90132
// To avoid the various off-by-one errors caused by the `/` characters, we keep using the path
91133
// determined above and can therefore skip parsing it here as well.
92-
let url = url::Url::parse(&format!("ssh://{host}"))?;
134+
let url = url::Url::parse(&format!("ssh://{host}")).map_err(|source| Error::Url {
135+
url: input.to_owned(),
136+
kind: UrlKind::Scp,
137+
source,
138+
})?;
93139

94140
Ok(crate::Url {
95141
serialize_alternative_form: true,

gix-url/tests/parse/invalid.rs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,42 @@
1-
use gix_url::parse::Error;
1+
use assert_matches::assert_matches;
22

3-
use crate::parse::assert_failure;
3+
use gix_url::parse::Error::*;
4+
5+
use crate::parse::parse;
46

57
#[test]
68
fn relative_path_due_to_double_colon() {
7-
assert_failure(
8-
"invalid:://host.xz/path/to/repo.git/",
9-
"Relative URLs are not permitted: \"invalid:://host.xz/path/to/repo.git/\"",
10-
)
9+
assert_matches!(parse("invalid:://host.xz/path/to/repo.git/"), Err(RelativeUrl { .. }))
1110
}
1211

1312
#[test]
1413
fn ssh_missing_path() {
15-
assert_failure("ssh://host.xz", Error::MissingRepositoryPath)
14+
assert_matches!(parse("ssh://host.xz"), Err(MissingRepositoryPath { .. }))
1615
}
1716

1817
#[test]
1918
fn git_missing_path() {
20-
assert_failure("git://host.xz", Error::MissingRepositoryPath)
19+
assert_matches!(parse("git://host.xz"), Err(MissingRepositoryPath { .. }))
2120
}
2221

2322
#[test]
2423
fn file_missing_path() {
25-
assert_failure("file://", Error::MissingRepositoryPath);
24+
assert_matches!(parse("file://"), Err(MissingRepositoryPath { .. }));
2625
}
2726

2827
#[test]
2928
fn empty() {
30-
assert_failure("", Error::MissingRepositoryPath);
31-
assert_failure("file://..", Error::MissingRepositoryPath);
32-
assert_failure("file://.", Error::MissingRepositoryPath);
29+
assert_matches!(parse(""), Err(MissingRepositoryPath { .. }));
30+
assert_matches!(parse("file://.."), Err(MissingRepositoryPath { .. }));
31+
assert_matches!(parse("file://."), Err(MissingRepositoryPath { .. }));
3332
#[cfg(not(windows))]
3433
{
35-
assert_failure("file://.\\", Error::MissingRepositoryPath);
34+
assert_matches!(parse("file://.\\"), Err(MissingRepositoryPath { .. }));
3635
}
37-
assert_failure("file://a", Error::MissingRepositoryPath);
36+
assert_matches!(parse("file://a"), Err(MissingRepositoryPath { .. }));
3837
}
3938

4039
#[test]
4140
fn missing_port_despite_indication() {
42-
assert_failure("ssh://host.xz:", Error::MissingRepositoryPath)
41+
assert_matches!(parse("ssh://host.xz:"), Err(MissingRepositoryPath { .. }))
4342
}

gix-url/tests/parse/mod.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use bstr::ByteSlice;
1+
use bstr::{BStr, ByteSlice};
22
use gix_url::Scheme;
33

44
fn assert_url(url: &str, expected: gix_url::Url) -> Result<gix_url::Url, crate::Error> {
@@ -22,11 +22,8 @@ fn assert_url_roundtrip(url: &str, expected: gix_url::Url) -> crate::Result {
2222
Ok(())
2323
}
2424

25-
fn assert_failure(url: &str, expected_err: impl ToString) {
26-
assert_eq!(
27-
gix_url::parse(url.into()).unwrap_err().to_string(),
28-
expected_err.to_string()
29-
);
25+
fn parse<'a>(input: impl Into<&'a BStr>) -> Result<gix_url::Url, gix_url::parse::Error> {
26+
gix_url::parse(input.into())
3027
}
3128

3229
fn url<'a, 'b>(

0 commit comments

Comments
 (0)