diff --git a/.github/assets/msrv-badge.svg b/.github/assets/msrv-badge.svg index e704d19..8345640 100644 --- a/.github/assets/msrv-badge.svg +++ b/.github/assets/msrv-badge.svg @@ -1 +1 @@ -rustc: 1.56.1+rustc1.56.1+ \ No newline at end of file +rustc: 1.60.0+rustc1.60.0+ \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e6bb345..ccb0cc2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,12 @@ name: CI on: - push: + merge_group: branches: - - staging - - trying + - main pull_request: - branches: [main] + branches: + - main workflow_dispatch: env: @@ -15,9 +15,10 @@ env: jobs: ci: name: CI - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: matrix: + os: [ubuntu-latest, windows-latest, macos-latest] cargo_checks: - name: Enforce default cargo fmt subcommand: fmt -- --check @@ -28,29 +29,27 @@ jobs: - name: Build subcommand: build --release --all-features --verbose steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Stable with rustfmt and clippy - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: - profile: minimal - toolchain: stable components: rustfmt, clippy - - uses: Swatinem/rust-cache@v1 + - uses: Swatinem/rust-cache@v2 - name: cargo check - ${{ matrix.cargo_checks.name }} run: cargo ${{ matrix.cargo_checks.subcommand }} - update-project-stuff: - if: github.event_name == 'push' && github.ref == 'refs/heads/staging' - uses: ./.github/workflows/update-repo-stuff.yml - secrets: inherit + #update-project-stuff: + # if: github.event_name == 'push' && github.ref == 'refs/heads/staging' + # uses: ./.github/workflows/update-repo-stuff.yml + # secrets: inherit - done: - name: Done - if: github.event_name == 'push' && github.ref == 'refs/heads/staging' - needs: - - ci - - update-project-stuff - runs-on: ubuntu-latest - steps: - - name: Done - run: echo "Done!" + #done: + # name: Done + # if: github.event_name == 'push' && github.ref == 'refs/heads/staging' + # needs: + # - ci + # - update-project-stuff + # runs-on: ubuntu-latest + # steps: + # - name: Done + # run: echo "Done!" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2be4028..3c2dd3b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -10,11 +10,8 @@ jobs: name: Publish to crates.io runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - - uses: katyo/publish-crates@v1 + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: katyo/publish-crates@v2 with: registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index bf49357..70b5096 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,26 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [UNRELEASED] +## [0.4.5](https://github.com/tjtelan/git-url-parse-rs/tree/v0.4.5) - 2024-09-06 + +### CI + +- Update changelog + +### Changed + +- Update MSRV badge +- Update README badges + +### Fixed + +- Add test for #51 + +### Other + +- Reduce required dependencies + +## [0.4.4](https://github.com/tjtelan/git-url-parse-rs/tree/v0.4.4) - 2022-11-05 ### Fixed @@ -54,13 +73,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Other -- Loosens dependency restrictions ([#12](https://github.com/tjtelan/git-url-parse-rs/issues/12)) ([#13](https://github.com/tjtelan/git-url-parse-rs/issues/13)) - Ci tune ([#18](https://github.com/tjtelan/git-url-parse-rs/issues/18)) ### Removed - Update dependencies and readme ([#23](https://github.com/tjtelan/git-url-parse-rs/issues/23)) +## [0.4.0](https://github.com/tjtelan/git-url-parse-rs/tree/v0.4.0) - 2021-11-14 + +### Added + +- Adding release dates in changelog +- Rename workflow + add workflow_dispatch to ci + +## [0.3.1](https://github.com/tjtelan/git-url-parse-rs/tree/v0.3.1) - 2021-01-27 + +### CI + +- Updating Changelog to prepare for v0.3.1 + +### Other + +- Loosens dependency restrictions ([#12](https://github.com/tjtelan/git-url-parse-rs/issues/12)) ([#13](https://github.com/tjtelan/git-url-parse-rs/issues/13)) + +## [0.3.0](https://github.com/tjtelan/git-url-parse-rs/tree/v0.3.0) - 2020-10-02 + +### Added + +- Adding schemas + ## [0.2.0](https://github.com/tjtelan/git-url-parse-rs/tree/v0.2.0) - 2020-05-13 ### Added diff --git a/Cargo.toml b/Cargo.toml index 4b6c907..9d7480d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,15 +9,18 @@ license = "MIT" name = "git-url-parse" readme = "README.md" repository = "https://github.com/tjtelan/git-url-parse-rs" -version = "0.4.4" +version = "0.4.5" + +[features] +default = [] +tracing = ["dep:tracing"] [dependencies] -tracing = "0.1" -url = "^2.2" -strum = "^0.24" -strum_macros = "^0.24" -color-eyre = "^0.6" -regex = "^1.4" +tracing = { version = "0.1", optional = true } +url = { version = "^2.2", default-features = false } +strum = { version = "^0.26", features = ["derive"] } +thiserror = "^1.0" [dev-dependencies] env_logger = "^0.9" +regex = "^1.10" diff --git a/README.md b/README.md index 1114ebf..b177a7c 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,10 @@ ![Minimum Supported Rust Version](https://raw.githubusercontent.com/tjtelan/git-url-parse-rs/main/.github/assets/msrv-badge.svg) [![Crates.io](https://img.shields.io/crates/v/git-url-parse)](https://crates.io/crates/git-url-parse) -![Crates.io](https://img.shields.io/crates/d/git-url-parse) -[![Github actions build status](https://github.com/tjtelan/git-url-parse-rs/workflows/git-url-parse/badge.svg)](https://github.com/tjtelan/git-url-parse-rs/actions/workflows/rust.yml) +[![Github actions CI status](https://github.com/tjtelan/git-url-parse-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/tjtelan/git-url-parse-rs/actions/workflows/ci.yml) [![docs.rs](https://docs.rs/git-url-parse/badge.svg)](https://docs.rs/git-url-parse/) -[![licence](https://img.shields.io/github/license/tjtelan/git-url-parse-rs)](LICENSE) -![Maintenance](https://img.shields.io/maintenance/yes/2022) +[![License](https://img.shields.io/github/license/tjtelan/git-url-parse-rs)](LICENSE) +![Maintenance](https://img.shields.io/maintenance/yes/2024) Supports common protocols as specified by the [Pro Git book](https://git-scm.com/book/en/v2) diff --git a/bors.toml b/bors.toml deleted file mode 100644 index 450460e..0000000 --- a/bors.toml +++ /dev/null @@ -1,6 +0,0 @@ -status = [ - "Done", -] -use_squash_merge = true -delete_merged_branches = true -timeout_sec = 900 # 15 mins \ No newline at end of file diff --git a/cliff.toml b/cliff.toml index 1a027e2..347c148 100644 --- a/cliff.toml +++ b/cliff.toml @@ -17,7 +17,11 @@ body = """ {% else %}\ ## [UNRELEASED] {% endif %}\ -{% for group, commits in commits | group_by(attribute="group") %} + +{% for group, commits in commits + | filter(attribute="merge_commit", value=false) + | unique(attribute="message") + | group_by(attribute="group") %} ### {{ group | upper_first }} {% for commit in commits %} - {{ commit.message | upper_first | split(pat="\n") | first }}\ @@ -33,49 +37,49 @@ footer = """ [git] # parse the commits based on https://www.conventionalcommits.org -conventional_commits = true +conventional_commits = true # filter out the commits that are not conventional -filter_unconventional = false +filter_unconventional = false commit_preprocessors = [ - { pattern = "\\(#([0-9]+)\\)", replace = "([#${1}](https://github.com/tjtelan/git-url-parse-rs/issues/${1}))"} + { pattern = "\\(#([0-9]+)\\)", replace = "([#${1}](https://github.com/tjtelan/git-url-parse-rs/issues/${1}))" }, ] # regex for parsing and grouping commits commit_parsers = [ - { message = ".*[Bb]ump", group = "Noise", skip = true}, - { message = ".*[Rr]evert", group = "Noise", skip = true}, - { message = ".*[Cl]ippy", group = "Noise", skip = true}, - { message = "^Merge pull request", group = "Noise", skip = true}, + { message = ".*[Bb]ump", group = "Noise", skip = true }, + { message = ".*[Rr]evert", group = "Noise", skip = true }, + { message = ".*[Cl]ippy", group = "Noise", skip = true }, + { message = "^Merge pull request", group = "Noise", skip = true }, - { message = "^test", group = "Fixed"}, - { message = "^.*[Ff]ix", group = "Fixed"}, - { message = "^[Rr]esolve", group = "Fixed"}, + { message = "^test", group = "Fixed" }, + { message = "^.*[Ff]ix", group = "Fixed" }, + { message = "^[Rr]esolve", group = "Fixed" }, - { message = "[Cc]ompile", group = "CI"}, - { message = "[Pp]ublish", group = "CI"}, + { message = "[Cc]ompile", group = "CI" }, + { message = "[Pp]ublish", group = "CI" }, - { message = ".*[Dd]eprecate", group = "Removed"}, - { message = "^[Dd]isable", group = "Removed"}, + { message = ".*[Dd]eprecate", group = "Removed" }, + { message = "^[Dd]isable", group = "Removed" }, - { message = ".*[Aa]dd", group = "Added"}, - { message = ".*[Ss]upport", group = "Added"}, - { message = ".*[Mm]ake", group = "Added"}, + { message = ".*[Aa]dd", group = "Added" }, + { message = ".*[Ss]upport", group = "Added" }, + { message = ".*[Mm]ake", group = "Added" }, - { message = ".*[Rr]emove", group = "Removed"}, - { message = ".*[Dd]elete", group = "Removed"}, - { message = ".*[Dd]isable", group = "Removed"}, + { message = ".*[Rr]emove", group = "Removed" }, + { message = ".*[Dd]elete", group = "Removed" }, + { message = ".*[Dd]isable", group = "Removed" }, - { message = "[Rr]elease", group = "CI"}, - { message = ".*[Ll]og", group = "CI"}, - { message = ".*[Bb]uild", group = "CI"}, + { message = "[Rr]elease", group = "CI" }, + { message = ".*[Ll]og", group = "CI" }, + { message = ".*[Bb]uild", group = "CI" }, - { message = ".*[Uu]pdate", group = "Changed"}, + { message = ".*[Uu]pdate", group = "Changed" }, - { message = ".*[Cc]lean", group = "Other"}, - { message = ".*[Rr]efactor", group = "Other"}, - { message = "^.*", group = "Other"}, + { message = ".*[Cc]lean", group = "Other" }, + { message = ".*[Rr]efactor", group = "Other" }, + { message = "^.*", group = "Other" }, ] # filter out the commits that are not matched by commit parsers diff --git a/examples/multi.rs b/examples/multi.rs index 4cc9d44..33b7bd9 100644 --- a/examples/multi.rs +++ b/examples/multi.rs @@ -1,7 +1,6 @@ -use color_eyre::Result; -use git_url_parse::GitUrl; +use git_url_parse::{GitUrl, GitUrlParseError}; -fn main() -> Result<()> { +fn main() -> Result<(), GitUrlParseError> { env_logger::init(); let test_vec = vec![ diff --git a/examples/trim_auth.rs b/examples/trim_auth.rs index 1b85967..8865c33 100644 --- a/examples/trim_auth.rs +++ b/examples/trim_auth.rs @@ -1,7 +1,6 @@ -use color_eyre::Result; -use git_url_parse::GitUrl; +use git_url_parse::{GitUrl, GitUrlParseError}; -fn main() -> Result<()> { +fn main() -> Result<(), GitUrlParseError> { env_logger::init(); let test_vec = vec![ diff --git a/src/lib.rs b/src/lib.rs index cad821d..08e4b93 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,14 @@ -use color_eyre::eyre::{eyre, WrapErr}; -pub use color_eyre::Result; -use regex::Regex; use std::fmt; use std::str::FromStr; -use strum_macros::{Display, EnumString, EnumVariantNames}; -use tracing::debug; +use strum::{Display, EnumString, VariantNames}; +use thiserror::Error; use url::Url; +#[cfg(feature = "tracing")] +use tracing::debug; + /// Supported uri schemes for parsing -#[derive(Debug, PartialEq, Eq, EnumString, EnumVariantNames, Clone, Display, Copy)] +#[derive(Debug, PartialEq, Eq, EnumString, VariantNames, Clone, Display, Copy)] #[strum(serialize_all = "kebab_case")] pub enum Scheme { /// Represents `file://` url scheme @@ -136,7 +136,7 @@ impl Default for GitUrl { } impl FromStr for GitUrl { - type Err = color_eyre::Report; + type Err = GitUrlParseError; fn from_str(s: &str) -> Result { GitUrl::parse(s) @@ -154,14 +154,22 @@ impl GitUrl { } /// Returns a `Result` after normalizing and parsing `url` for metadata - pub fn parse(url: &str) -> Result { + pub fn parse(url: &str) -> Result { // Normalize the url so we can use Url crate to process ssh urls - let normalized = normalize_url(url) - .with_context(|| "Url normalization into url::Url failed".to_string())?; + let normalized = if let Ok(url) = normalize_url(url) { + url + } else { + return Err(GitUrlParseError::UrlNormalizeFailed); + }; // Some pre-processing for paths - let scheme = Scheme::from_str(normalized.scheme()) - .with_context(|| format!("Scheme unsupported: {:?}", normalized.scheme()))?; + let scheme = if let Ok(scheme) = Scheme::from_str(normalized.scheme()) { + scheme + } else { + return Err(GitUrlParseError::UnsupportedScheme( + normalized.scheme().to_string(), + )); + }; // Normalized ssh urls can always have their first '/' removed let urlpath = match &scheme { @@ -177,6 +185,7 @@ impl GitUrl { // Parse through path for name,owner,organization // Support organizations for Azure Devops + #[cfg(feature = "tracing")] debug!("The urlpath: {:?}", &urlpath); // Most git services use the path for metadata in the same way, so we're going to separate @@ -187,10 +196,14 @@ impl GitUrl { // // organizations are going to be supported on a per-host basis let splitpath = &urlpath.rsplit_terminator('/').collect::>(); + + #[cfg(feature = "tracing")] debug!("rsplit results for metadata: {:?}", splitpath); let name = splitpath[0].trim_end_matches(".git").to_string(); + // TODO: I think here is where we want to update the url pattern identification step.. I want to be able to have a hint that the user can pass + let (owner, organization, fullname) = match &scheme { // We're not going to assume anything about metadata from a filepath Scheme::File => (None::, None::, name.clone()), @@ -201,12 +214,15 @@ impl GitUrl { let hosts_w_organization_in_path = vec!["dev.azure.com", "ssh.dev.azure.com"]; //vec!["dev.azure.com", "ssh.dev.azure.com", "visualstudio.com"]; - let host_str = normalized - .host_str() - .ok_or(eyre!("Host from URL could not be represented as str"))?; + let host_str = if let Some(host) = normalized.host_str() { + host + } else { + return Err(GitUrlParseError::UnsupportedUrlHostFormat); + }; match hosts_w_organization_in_path.contains(&host_str) { true => { + #[cfg(feature = "tracing")] debug!("Found a git provider with an org"); // The path differs between git:// and https:// schemes @@ -242,16 +258,18 @@ impl GitUrl { fullname.join("/"), ) } - _ => return Err(eyre!("Scheme not supported for host")), + + // TODO: I'm not sure if I want to support throwing this error long-term + _ => return Err(GitUrlParseError::UnexpectedScheme), } } false => { if !url.starts_with("ssh") && splitpath.len() < 2 { - return Err(eyre!("git url is not of expected format")); + return Err(GitUrlParseError::UnexpectedFormat); } let position = match splitpath.len() { - 0 => return Err(eyre!("git url is not of expected format")), + 0 => return Err(GitUrlParseError::UnexpectedFormat), 1 => 0, _ => 1, }; @@ -313,19 +331,21 @@ impl GitUrl { /// Prepends `ssh://` to url /// /// Supports absolute and relative paths -fn normalize_ssh_url(url: &str) -> Result { +fn normalize_ssh_url(url: &str) -> Result { let u = url.split(':').collect::>(); match u.len() { 2 => { + #[cfg(feature = "tracing")] debug!("Normalizing ssh url: {:?}", u); normalize_url(&format!("ssh://{}/{}", u[0], u[1])) } 3 => { + #[cfg(feature = "tracing")] debug!("Normalizing ssh url with ports: {:?}", u); normalize_url(&format!("ssh://{}:{}/{}", u[0], u[1], u[2])) } - _default => Err(eyre!("SSH normalization pattern not covered for: {:?}", u)), + _default => Err(GitUrlParseError::UnsupportedSshUrlFormat), } } @@ -333,41 +353,47 @@ fn normalize_ssh_url(url: &str) -> Result { /// /// Prepends `file://` to url #[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))] -fn normalize_file_path(filepath: &str) -> Result { +fn normalize_file_path(filepath: &str) -> Result { let fp = Url::from_file_path(filepath); match fp { Ok(path) => Ok(path), - Err(_e) => Ok(normalize_url(&format!("file://{}", filepath)) - .with_context(|| "file:// normalization failed".to_string())?), + Err(_e) => { + if let Ok(file_url) = normalize_url(&format!("file://{}", filepath)) { + Ok(file_url) + } else { + return Err(GitUrlParseError::FileUrlNormalizeFailedSchemeAdded); + } + } } } #[cfg(target_arch = "wasm32")] -fn normalize_file_path(_filepath: &str) -> Result { +fn normalize_file_path(_filepath: &str) -> Result { unreachable!() } /// `normalize_url` takes in url as `&str` and takes an opinionated approach to identify /// `ssh://` or `file://` urls that require more information to be added so that /// they can be parsed more effectively by `url::Url::parse()` -pub fn normalize_url(url: &str) -> Result { +pub fn normalize_url(url: &str) -> Result { + #[cfg(feature = "tracing")] debug!("Processing: {:?}", &url); + // TODO: Should this be extended to check for any whitespace? // Error if there are null bytes within the url // https://github.com/tjtelan/git-url-parse-rs/issues/16 if url.contains('\0') { - return Err(eyre!("Found null bytes within input url before parsing")); + return Err(GitUrlParseError::FoundNullBytes); } // We're going to remove any trailing slash before running through Url::parse let trim_url = url.trim_end_matches('/'); + // TODO: Remove support for this form when I go to next major version. + // I forget what it supports, and it isn't obvious after searching for examples // normalize short git url notation: git:host/path - let url_to_parse = if Regex::new(r"^git:[^/]") - .with_context(|| "Failed to build short git url regex for testing against url".to_string())? - .is_match(trim_url) - { + let url_to_parse = if trim_url.starts_with("git:") && !trim_url.starts_with("git://") { trim_url.replace("git:", "git://") } else { trim_url.to_string() @@ -381,37 +407,126 @@ pub fn normalize_url(url: &str) -> Result { Ok(_p) => u, Err(_e) => { // Catch case when an ssh url is given w/o a user + #[cfg(feature = "tracing")] debug!("Scheme parse fail. Assuming a userless ssh url"); - normalize_ssh_url(trim_url).with_context(|| { - "No url scheme was found, then failed to normalize as ssh url.".to_string() - })? + if let Ok(ssh_url) = normalize_ssh_url(trim_url) { + ssh_url + } else { + return Err(GitUrlParseError::SshUrlNormalizeFailedNoScheme); + } } } } - Err(url::ParseError::RelativeUrlWithoutBase) => { - // If we're here, we're only looking for Scheme::Ssh or Scheme::File + // If we're here, we're only looking for Scheme::Ssh or Scheme::File + // TODO: Add test for this + Err(url::ParseError::RelativeUrlWithoutBase) => { // Assuming we have found Scheme::Ssh if we can find an "@" before ":" // Otherwise we have Scheme::File - let re = Regex::new(r"^\S+(@)\S+(:).*$").with_context(|| { - "Failed to build ssh git url regex for testing against url".to_string() - })?; + //let re = Regex::new(r"^\S+(@)\S+(:).*$").with_context(|| { + // "Failed to build ssh git url regex for testing against url".to_string() + //})?; - match re.is_match(trim_url) { + match is_ssh_url(trim_url) { true => { + #[cfg(feature = "tracing")] debug!("Scheme::SSH match for normalization"); - normalize_ssh_url(trim_url) - .with_context(|| "Failed to normalize as ssh url".to_string())? + normalize_ssh_url(trim_url)? } false => { + #[cfg(feature = "tracing")] debug!("Scheme::File match for normalization"); - normalize_file_path(trim_url) - .with_context(|| "Failed to normalize as file url".to_string())? + normalize_file_path(trim_url)? } } } Err(err) => { - return Err(eyre!("url parsing failed: {:?}", err)); + return Err(GitUrlParseError::from(err)); } }) } + +// Valid ssh `url` for cloning have a usernames, +// but we don't require it classification or parsing purposes +// However a path must be specified with a `:` +fn is_ssh_url(url: &str) -> bool { + // if we do not have a path + if !url.contains(':') { + return false; + } + + // if we have a username, expect it before the path (Are usernames with colons valid?) + if let (Some(at_pos), Some(colon_pos)) = (url.find('@'), url.find(':')) { + if colon_pos < at_pos { + return false; + } + + // Make sure we provided a username, and not just `@` + let parts: Vec<&str> = url.split('@').collect(); + if parts.len() != 2 && !parts[0].is_empty() { + return false; + } else { + return true; + } + } + + // it's an ssh url if we have a domain:path pattern + let parts: Vec<&str> = url.split(':').collect(); + + // FIXME: I am not sure how to validate a url with a port + //if parts.len() != 3 && !parts[0].is_empty() && !parts[1].is_empty() && !parts[2].is_empty() { + // return false; + //} + + // This should also handle if a port is specified + // no port example: ssh://user@domain:path/to/repo.git + // port example: ssh://user@domain:port/path/to/repo.git + if parts.len() != 2 && !parts[0].is_empty() && !parts[1].is_empty() { + return false; + } else { + return true; + } +} + +#[derive(Error, Debug, PartialEq, Eq)] +pub enum GitUrlParseError { + #[error("Error from Url crate")] + UrlParseError(#[from] url::ParseError), + + #[error("Url normalization into url::Url failed")] + UrlNormalizeFailed, + + #[error("No url scheme was found, then failed to normalize as ssh url.")] + SshUrlNormalizeFailedNoScheme, + + #[error("No url scheme was found, then failed to normalize as ssh url after adding 'ssh://'")] + SshUrlNormalizeFailedSchemeAdded, + + #[error("Failed to normalize as ssh url after adding 'ssh://'")] + SshUrlNormalizeFailedSchemeAddedWithPorts, + + #[error("No url scheme was found, then failed to normalize as file url.")] + FileUrlNormalizeFailedNoScheme, + + #[error( + "No url scheme was found, then failed to normalize as file url after adding 'file://'" + )] + FileUrlNormalizeFailedSchemeAdded, + + #[error("Git Url not in expected format")] + UnexpectedFormat, + + // FIXME: Keep an eye on this error for removal + #[error("Git Url for host using unexpected scheme")] + UnexpectedScheme, + + #[error("Scheme unsupported: {0}")] + UnsupportedScheme(String), + #[error("Host from Url cannot be str or does not exist")] + UnsupportedUrlHostFormat, + #[error("Git Url not in expected format for SSH")] + UnsupportedSshUrlFormat, + + #[error("Found null bytes within input url before parsing")] + FoundNullBytes, +} diff --git a/tests/normalize.rs b/tests/normalize.rs index 5dbc010..1ffd37b 100644 --- a/tests/normalize.rs +++ b/tests/normalize.rs @@ -9,6 +9,8 @@ fn git() { assert_eq!(normalized.as_str(), "git://host.tld/user/project-name.git"); } +// I'm not even sure if this is a form that should be supported bc I can't find examples of it being used in the wild by another service +//#[should_panic] #[test] fn git2() { let test_url = "git:host.tld/user/project-name.git"; @@ -98,7 +100,7 @@ fn unix_file_no_scheme_rel_path() { assert_eq!(normalized.as_str(), "file://../user/project-name.git"); } -#[should_panic(expected = "assertion failed: `(left == right)")] +#[should_panic] #[test] fn win_file_scheme_abs_path() { let test_url = "file://c:\\user\\project-name.git"; @@ -108,7 +110,7 @@ fn win_file_scheme_abs_path() { assert_eq!(normalized.as_str(), "file://c:\\user\\project-name.git"); } -#[should_panic(expected = "assertion failed: `(left == right)")] +#[should_panic] #[test] fn win_file_no_scheme_abs_path() { let test_url = "c:\\user\\project-name.git"; @@ -163,3 +165,21 @@ fn null_in_input2() { assert!(normalized.is_err()); } + +// From https://github.com/tjtelan/git-url-parse-rs/issues/51 +#[test] +fn large_bad_input1() { + let test_url = std::iter::repeat("g@1::::").take(10000).collect::(); + let normalized = normalize_url(&test_url); + + assert!(normalized.is_err()); +} + +// From https://github.com/tjtelan/git-url-parse-rs/issues/51 +#[test] +fn large_bad_input2() { + let test_url = std::iter::repeat(":").take(10000).collect::(); + let normalized = normalize_url(&test_url); + + assert!(normalized.is_err()); +} diff --git a/tests/parse.rs b/tests/parse.rs index 4bc55a4..deabb37 100644 --- a/tests/parse.rs +++ b/tests/parse.rs @@ -310,7 +310,7 @@ fn relative_windows_path() { } // Issue #7 - Absolute Windows paths will not parse at all -#[should_panic(expected = "git url is not of expected format")] +#[should_panic(expected = "URL parse failed: UnexpectedFormat")] #[test] fn absolute_windows_path() { let test_url = "c:\\project-name.git"; @@ -341,7 +341,7 @@ fn ssh_user_path_not_acctname_reponame_format() { assert!(e.is_err()); assert_eq!( format!("{}", e.err().unwrap()), - "git url is not of expected format" + "Git Url not in expected format" ); }