Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,9 @@ Options:
Basically, the base URL option resolves links as if the local files were hosted
at the given base URL address.

The provided base URL value must either be a URL (with scheme) or an absolute path.
Note that certain URL schemes cannot be used as a base, e.g., `data` and `mailto`.

--root-dir <ROOT_DIR>
Root directory to use when checking absolute links in local files. This option is
required if absolute links appear in local files, otherwise those links will be
Expand Down
5 changes: 4 additions & 1 deletion lychee-bin/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,10 @@ of the base URL. For example, a base URL of `https://example.com/dir/page/` and
a link of `a` would resolve to `https://example.com/dir/page/a`.

Basically, the base URL option resolves links as if the local files were hosted
at the given base URL address."
at the given base URL address.

The provided base URL value must either be a URL (with scheme) or an absolute path.
Note that certain URL schemes cannot be used as a base, e.g., `data` and `mailto`."
)]
#[serde(default)]
pub(crate) base_url: Option<Base>,
Expand Down
16 changes: 14 additions & 2 deletions lychee-bin/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,20 @@ pub(crate) fn parse_remaps(remaps: &[String]) -> Result<Remaps> {
.context("Remaps must be of the form '<pattern> <uri>' (separated by whitespace)")
}

pub(crate) fn parse_base(src: &str) -> Result<Base, lychee_lib::ErrorKind> {
Base::try_from(src)
pub(crate) fn parse_base(src: &str) -> Result<Base> {
match Base::try_from(src) {
Ok(x) => Ok(x),
Err(e) => {
// if context is defined, clap displays only the context string in
// argument parse errors. to keep the message from within InvalidBase,
// we need to retain it manually.
let message = format!(
"{e}. See `--help` for more information. If you want to resolve \
root-relative links in local files, also see `--root-dir`."
);
Err(e).context(message)
}
}
}

#[cfg(test)]
Expand Down
27 changes: 24 additions & 3 deletions lychee-lib/src/types/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,24 @@ impl TryFrom<&str> for Base {
if url.cannot_be_a_base() {
return Err(ErrorKind::InvalidBase(
value.to_string(),
"The given URL cannot be a base".to_string(),
"The given URL cannot be used as a base URL".to_string(),
));
}
return Ok(Self::Remote(url));
}
Ok(Self::Local(PathBuf::from(value)))

// require absolute paths in `Base::Local`. a local non-relative base is
// basically useless because it cannot be used to join URLs and will
// cause InvalidBaseJoin.
let path = PathBuf::from(value);
if path.is_absolute() {
Ok(Self::Local(path))
} else {
Err(ErrorKind::InvalidBase(
value.to_string(),
"Base must either be a URL (with scheme) or an absolute local path".to_string(),
))
}
}
}

Expand Down Expand Up @@ -96,14 +108,23 @@ mod test_base {

#[test]
fn test_valid_local_path_string_as_base() -> Result<()> {
let cases = vec!["/tmp/lychee", "/tmp/lychee/", "tmp/lychee/"];
let cases = vec!["/tmp/lychee", "/tmp/lychee/"];

for case in cases {
assert_eq!(Base::try_from(case)?, Base::Local(PathBuf::from(case)));
}
Ok(())
}

#[test]
fn test_invalid_local_path_string_as_base() {
let cases = vec!["a", "tmp/lychee/", "example.com", "../nonlocal"];

for case in cases {
assert!(Base::try_from(case).is_err());
}
}

#[test]
fn test_valid_local() -> Result<()> {
let dir = tempfile::tempdir().unwrap();
Expand Down