Skip to content

Commit

Permalink
Merge pull request #187 from jwodder/is-valid-funcs
Browse files Browse the repository at this point in the history
Move & add to `is_valid_*()`
  • Loading branch information
jwodder authored Feb 16, 2024
2 parents 8067c47 + 8740351 commit 5866004
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 146 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ v0.6.0 (in development)
- `PartialOrd<str>`
- `TryFrom<String>`
- Added `GHRepo::as_str()` method
- **Breaking**: The `GHRepo::is_valid_owner()` and `GHRepo::is_valid_name()`
methods are now regular functions
- Added `is_valid_repository()` function

v0.5.0 (2023-04-27)
-------------------
Expand Down
121 changes: 67 additions & 54 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@ impl GHRepo {
/// If `owner` is not a valid GitHub owner name, or if `name` is not a
/// valid GitHub repository name, returns [`ParseError`].
pub fn new(owner: &str, name: &str) -> Result<Self, ParseError> {
if !GHRepo::is_valid_owner(owner) {
if !is_valid_owner(owner) {
Err(ParseError::InvalidOwner(owner.to_string()))
} else if !GHRepo::is_valid_name(name) {
} else if !is_valid_name(name) {
Err(ParseError::InvalidName(name.to_string()))
} else {
Ok(GHRepo {
Expand All @@ -141,57 +141,6 @@ impl GHRepo {
}
}

/// Test whether a string is a valid GitHub user login or organization
/// name.
///
/// As of 2017-07-23, trying to sign up to GitHub with an invalid username
/// or create an organization with an invalid name gives the message
/// "Username may only contain alphanumeric characters or single hyphens,
/// and cannot begin or end with a hyphen". Additionally, trying to create
/// a user named "none" (case insensitive) gives the message "Username name
/// 'none' is a reserved word." Unfortunately, there are a number of users
/// who made accounts before the current name restrictions were put in
/// place, and so this method also needs to accept names that contain
/// underscores, contain multiple consecutive hyphens, begin with a hyphen,
/// and/or end with a hyphen.
///
/// As this function endeavors to accept all usernames that were valid at
/// any point, just because a name is accepted doesn't necessarily mean you
/// can create a user by that name on GitHub today.
///
/// # Example
///
/// ```
/// # use ghrepo::GHRepo;
/// assert!(GHRepo::is_valid_owner("octocat"));
/// assert!(GHRepo::is_valid_owner("octo-cat"));
/// assert!(!GHRepo::is_valid_owner("octo.cat"));
/// assert!(!GHRepo::is_valid_owner("octocat/repository"));
/// assert!(!GHRepo::is_valid_owner("none"));
/// ```
pub fn is_valid_owner(s: &str) -> bool {
matches!(split_owner(s), Some((_, "")))
}

/// Test whether a string is a valid repository name.
///
/// Testing as of 2017-05-21 indicates that repository names can be
/// composed of alphanumeric ASCII characters, hyphens, periods, and/or
/// underscores, with the names `.` and `..` being reserved and names
/// ending with `.git` (case insensitive) forbidden.
///
/// # Example
///
/// ```
/// # use ghrepo::GHRepo;
/// assert!(GHRepo::is_valid_name("my-repo"));
/// assert!(!GHRepo::is_valid_name("my-repo.git"));
/// assert!(!GHRepo::is_valid_owner("octocat/my-repo"));
/// ```
pub fn is_valid_name(s: &str) -> bool {
matches!(split_name(s), Some((_, "")))
}

/// Like [`GHRepo::from_str()`], except that if `s` is just a repository
/// name without an owner, the owner will be set to `owner`
///
Expand All @@ -217,7 +166,7 @@ impl GHRepo {
/// # }
/// ```
pub fn from_str_with_owner(s: &str, owner: &str) -> Result<Self, ParseError> {
if GHRepo::is_valid_name(s) {
if is_valid_name(s) {
GHRepo::new(owner, s)
} else {
GHRepo::from_str(s)
Expand Down Expand Up @@ -680,3 +629,67 @@ impl From<ParseError> for LocalRepoError {
LocalRepoError::InvalidRemoteURL(e)
}
}

/// Test whether a string is a valid GitHub user login or organization name.
///
/// As of 2017-07-23, trying to sign up to GitHub with an invalid username or
/// create an organization with an invalid name gives the message "Username may
/// only contain alphanumeric characters or single hyphens, and cannot begin or
/// end with a hyphen". Additionally, trying to create a user named "none"
/// (case insensitive) gives the message "Username name 'none' is a reserved
/// word." Unfortunately, there are a number of users who made accounts before
/// the current name restrictions were put in place, and so this method also
/// needs to accept names that contain underscores, contain multiple
/// consecutive hyphens, begin with a hyphen, and/or end with a hyphen.
///
/// As this function endeavors to accept all usernames that were valid at any
/// point, just because a name is accepted doesn't necessarily mean you can
/// create a user by that name on GitHub today.
///
/// # Example
///
/// ```
/// # use ghrepo::is_valid_owner;
/// assert!(is_valid_owner("octocat"));
/// assert!(is_valid_owner("octo-cat"));
/// assert!(!is_valid_owner("octo.cat"));
/// assert!(!is_valid_owner("octocat/repository"));
/// assert!(!is_valid_owner("none"));
/// ```
pub fn is_valid_owner(s: &str) -> bool {
matches!(split_owner(s), Some((_, "")))
}

/// Test whether a string is a valid repository name.
///
/// Testing as of 2017-05-21 indicates that repository names can be composed of
/// alphanumeric ASCII characters, hyphens, periods, and/or underscores, with
/// the names `.` and `..` being reserved and names ending with `.git` (case
/// insensitive) forbidden.
///
/// # Example
///
/// ```
/// # use ghrepo::is_valid_name;
/// assert!(is_valid_name("my-repo"));
/// assert!(!is_valid_name("my-repo.git"));
/// assert!(!is_valid_name("octocat/my-repo"));
/// ```
pub fn is_valid_name(s: &str) -> bool {
matches!(split_name(s), Some((_, "")))
}

/// Test whether a string is a valid repository specifier/full name of the form
/// `{owner}/{name}`.
///
/// # Example
///
/// ```
/// # use ghrepo::is_valid_repository;
/// assert!(is_valid_repository("octocat/my-repo"));
/// assert!(!is_valid_repository("octocat/my-repo.git"));
/// assert!(!is_valid_repository("my-repo"));
/// ```
pub fn is_valid_repository(s: &str) -> bool {
matches!(split_owner_name(s), Some((_, _, "")))
}
92 changes: 0 additions & 92 deletions tests/ghrepo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,98 +74,6 @@ fn test_ssh_url() {
assert_eq!(r.ssh_url().parse::<GHRepo>(), Ok(r));
}

#[rstest]
#[case("steven-universe")]
#[case("steven")]
#[case("s")]
#[case("s-u")]
#[case("7152")]
#[case("s-t-e-v-e-n")]
#[case("s-t-eeeeee-v-e-n")]
#[case("peridot-2F5L-5XG")]
#[case("nonely")]
#[case("none-one")]
#[case("none-none")]
#[case("nonenone")]
#[case("none0")]
#[case("0none")]
// The following are actual usernames on GitHub that violate the current
// username restrictions:
#[case("-")]
#[case("-Jerry-")]
#[case("-SFT-Clan")]
#[case("123456----")]
#[case("FirE-Fly-")]
#[case("None-")]
#[case("alex--evil")]
#[case("johan--")]
#[case("pj_nitin")]
#[case("up_the_irons")]
fn test_good_owner(#[case] owner: &str) {
assert!(GHRepo::is_valid_owner(owner));
}

#[rstest]
#[case("steven.universe")]
#[case("steven-universe@beachcity.dv")]
#[case("steven-univerß")]
#[case("")]
#[case("none")]
#[case("NONE")]
fn test_bad_owner(#[case] owner: &str) {
assert!(!GHRepo::is_valid_owner(owner));
}

#[rstest]
#[case("steven-universe")]
#[case("steven")]
#[case("s")]
#[case("s-u")]
#[case("7152")]
#[case("s-t-e-v-e-n")]
#[case("s-t-eeeeee-v-e-n")]
#[case("peridot-2F5L-5XG")]
#[case("...")]
#[case("-steven")]
#[case("steven-")]
#[case("-steven-")]
#[case("steven.universe")]
#[case("steven_universe")]
#[case("steven--universe")]
#[case("s--u")]
#[case("git.steven")]
#[case("steven.git.txt")]
#[case("steven.gitt")]
#[case(".gitt")]
#[case("..gitt")]
#[case("...gitt")]
#[case("git")]
#[case("-")]
#[case("_")]
#[case("---")]
#[case(".---")]
#[case(".steven")]
#[case("..steven")]
#[case("...steven")]
fn test_good_name(#[case] name: &str) {
assert!(GHRepo::is_valid_name(name));
}

#[rstest]
#[case("steven-univerß")]
#[case(".")]
#[case("..")]
#[case("...git")]
#[case("..git")]
#[case(".git")]
#[case("")]
#[case("steven.git")]
#[case("steven.GIT")]
#[case("steven.Git")]
fn test_bad_name(#[case] name: &str) {
assert!(!GHRepo::is_valid_name(name));
}

#[template]
#[rstest]
#[case("git://github.com/jwodder/headerparser", "jwodder", "headerparser")]
Expand Down
112 changes: 112 additions & 0 deletions tests/valid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#![allow(clippy::items_after_test_module)]
use ghrepo::{is_valid_name, is_valid_owner, is_valid_repository};
use rstest::rstest;

#[rstest]
#[case("steven-universe")]
#[case("steven")]
#[case("s")]
#[case("s-u")]
#[case("7152")]
#[case("s-t-e-v-e-n")]
#[case("s-t-eeeeee-v-e-n")]
#[case("peridot-2F5L-5XG")]
#[case("nonely")]
#[case("none-one")]
#[case("none-none")]
#[case("nonenone")]
#[case("none0")]
#[case("0none")]
// The following are actual usernames on GitHub that violate the current
// username restrictions:
#[case("-")]
#[case("-Jerry-")]
#[case("-SFT-Clan")]
#[case("123456----")]
#[case("FirE-Fly-")]
#[case("None-")]
#[case("alex--evil")]
#[case("johan--")]
#[case("pj_nitin")]
#[case("up_the_irons")]
fn test_good_owner(#[case] owner: &str) {
assert!(is_valid_owner(owner));
}

#[rstest]
#[case("steven.universe")]
#[case("steven-universe@beachcity.dv")]
#[case("steven-univerß")]
#[case("steven/universe")]
#[case("")]
#[case("none")]
#[case("NONE")]
fn test_bad_owner(#[case] owner: &str) {
assert!(!is_valid_owner(owner));
}

#[rstest]
#[case("steven-universe")]
#[case("steven")]
#[case("s")]
#[case("s-u")]
#[case("7152")]
#[case("s-t-e-v-e-n")]
#[case("s-t-eeeeee-v-e-n")]
#[case("peridot-2F5L-5XG")]
#[case("...")]
#[case("-steven")]
#[case("steven-")]
#[case("-steven-")]
#[case("steven.universe")]
#[case("steven_universe")]
#[case("steven--universe")]
#[case("s--u")]
#[case("git.steven")]
#[case("steven.git.txt")]
#[case("steven.gitt")]
#[case(".gitt")]
#[case("..gitt")]
#[case("...gitt")]
#[case("git")]
#[case("-")]
#[case("_")]
#[case("---")]
#[case(".---")]
#[case(".steven")]
#[case("..steven")]
#[case("...steven")]
fn test_good_name(#[case] name: &str) {
assert!(is_valid_name(name));
}

#[rstest]
#[case("steven-univerß")]
#[case(".")]
#[case("..")]
#[case("...git")]
#[case("..git")]
#[case(".git")]
#[case("")]
#[case("steven.git")]
#[case("steven.GIT")]
#[case("steven.Git")]
#[case("steven/universe")]
fn test_bad_name(#[case] name: &str) {
assert!(!is_valid_name(name));
}

#[rstest]
#[case("steven/universe")]
fn test_good_repository(#[case] spec: &str) {
assert!(is_valid_repository(spec));
}

#[rstest]
#[case("steven/universe.git")]
#[case("steven/universe/main")]
#[case("https://github.com/steven/universe")]
#[case("steven")]
fn test_bad_repository(#[case] spec: &str) {
assert!(!is_valid_repository(spec));
}

0 comments on commit 5866004

Please sign in to comment.