diff --git a/.gitignore b/.gitignore index ea8c4bf..f7dfd22 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,14 @@ -/target +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# IntelliJ generated files +*.idea diff --git a/src/extract.rs b/src/extract.rs index b6f7202..020b6c7 100644 --- a/src/extract.rs +++ b/src/extract.rs @@ -286,6 +286,49 @@ mod test { assert_eq!(links, HashSet::new()) } + #[test] + fn test_markdown_internal_url() { + let base_url = "https://localhost.com/"; + let input = "This is [an internal url](@/internal.md) \ + This is [an internal url](@/internal.markdown) \ + This is [an internal url](@/internal.markdown#example) \ + This is [an internal url](@/internal.md#example)"; + let links: HashSet = extract_links( + &InputContent::from_string(input, FileType::Markdown), + Some(Url::parse(base_url).unwrap()), + ) + .into_iter() + .map(|r| r.uri) + .collect(); + + let expected = [ + website("https://localhost.com/@/internal.md"), + website("https://localhost.com/@/internal.markdown"), + website("https://localhost.com/@/internal.md#example"), + website("https://localhost.com/@/internal.markdown#example"), + ] + .iter() + .cloned() + .collect(); + + assert_eq!(links, expected) + } + + #[test] + fn test_skip_markdown_email() { + let input = "Get in touch - [Contact Us](mailto:test@test.com)"; + let links: HashSet = + extract_links(&InputContent::from_string(input, FileType::Markdown), None) + .into_iter() + .map(|r| r.uri) + .collect(); + let expected: HashSet = [Uri::Mail("test@test.com".to_string())] + .iter() + .cloned() + .collect(); + assert_eq!(links, expected) + } + #[test] fn test_non_markdown_links() { let input = diff --git a/src/uri.rs b/src/uri.rs index 9e2b271..57ff67c 100644 --- a/src/uri.rs +++ b/src/uri.rs @@ -40,19 +40,28 @@ impl Uri { } } +fn is_internal_link(link: &str) -> bool { + // The first element should contain the Markdown file link + // @see https://www.markdownguide.org/basic-syntax/#links + let anchor_links = link.split('#').next().unwrap_or(""); + anchor_links.ends_with(".md") | anchor_links.ends_with(".markdown") +} + impl TryFrom<&str> for Uri { type Error = anyhow::Error; fn try_from(s: &str) -> Result { + // Check for internal Markdown links + let is_link_internal = is_internal_link(s); // Remove the `mailto` scheme if it exists // to avoid parsing it as a website URL. let s = s.trim_start_matches("mailto:"); + if s.contains('@') & !is_link_internal { + return Ok(Uri::Mail(s.to_string())); + } if let Ok(uri) = Url::parse(s) { return Ok(Uri::Website(uri)); }; - if s.contains('@') { - return Ok(Uri::Mail(s.to_string())); - }; bail!("Cannot convert to Uri") } }