Skip to content

Commit 5487a54

Browse files
committed
Add bindings for git_reference_name_is_valid, git_remote_name_is_valid & git_tag_name_is_valid
1 parent 49879e9 commit 5487a54

File tree

5 files changed

+116
-6
lines changed

5 files changed

+116
-6
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
target
22
Cargo.lock
3-
src/main.rs
3+
src/main.rs

libgit2-sys/lib.rs

+15
Original file line numberDiff line numberDiff line change
@@ -2246,7 +2246,14 @@ extern "C" {
22462246
remote: *const git_remote,
22472247
) -> c_int;
22482248
pub fn git_remote_get_refspec(remote: *const git_remote, n: size_t) -> *const git_refspec;
2249+
2250+
// This function is no longer used by kept here for the sake of completeness and to ensure
2251+
// no one thinks of it as "missing" in terms of FFI coverage. A conscious decision was made to
2252+
// use `git_remote_name_is_valid` as opposed to this function when trying to figure out whether
2253+
// a remote name is valid. More details at -- https://github.com/rust-lang/git2-rs/pull/882
22492254
pub fn git_remote_is_valid_name(remote_name: *const c_char) -> c_int;
2255+
2256+
pub fn git_remote_name_is_valid(valid: *mut c_int, remote_name: *const c_char) -> c_int;
22502257
pub fn git_remote_list(out: *mut git_strarray, repo: *mut git_repository) -> c_int;
22512258
pub fn git_remote_rename(
22522259
problems: *mut git_strarray,
@@ -2397,7 +2404,14 @@ extern "C" {
23972404
pub fn git_reference_is_note(r: *const git_reference) -> c_int;
23982405
pub fn git_reference_is_remote(r: *const git_reference) -> c_int;
23992406
pub fn git_reference_is_tag(r: *const git_reference) -> c_int;
2407+
2408+
// This function is no longer used by kept here for the sake of completeness and to ensure
2409+
// no one thinks of it as "missing" in terms of FFI coverage. A conscious decision was made to
2410+
// use `git_reference_name_is_valid` as opposed to this function when trying to figure out whether
2411+
// a ref name is valid. More details at -- https://github.com/rust-lang/git2-rs/pull/882
24002412
pub fn git_reference_is_valid_name(name: *const c_char) -> c_int;
2413+
2414+
pub fn git_reference_name_is_valid(valid: *mut c_int, refname: *const c_char) -> c_int;
24012415
pub fn git_reference_lookup(
24022416
out: *mut *mut git_reference,
24032417
repo: *mut git_repository,
@@ -3209,6 +3223,7 @@ extern "C" {
32093223
pub fn git_tag_target(target_out: *mut *mut git_object, tag: *const git_tag) -> c_int;
32103224
pub fn git_tag_target_id(tag: *const git_tag) -> *const git_oid;
32113225
pub fn git_tag_target_type(tag: *const git_tag) -> git_object_t;
3226+
pub fn git_tag_name_is_valid(valid: *mut c_int, tag_name: *const c_char) -> c_int;
32123227

32133228
// checkout
32143229
pub fn git_checkout_head(repo: *mut git_repository, opts: *const git_checkout_options)

src/reference.rs

+34-3
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,28 @@ impl<'repo> Reference<'repo> {
6262
pub fn is_valid_name(refname: &str) -> bool {
6363
crate::init();
6464
let refname = CString::new(refname).unwrap();
65-
unsafe { raw::git_reference_is_valid_name(refname.as_ptr()) == 1 }
65+
let mut valid: libc::c_int = 0;
66+
let return_code = unsafe {
67+
raw::git_reference_name_is_valid(
68+
&mut valid,
69+
refname.as_ptr()
70+
)
71+
};
72+
return_code >= 0 && valid == 1
73+
}
74+
75+
/// Ensure the reference name is well-formed.
76+
pub fn name_is_valid(refname: &str) -> Result<bool, Error> {
77+
crate::init();
78+
let refname = CString::new(refname)?;
79+
let mut valid: libc::c_int = 0;
80+
unsafe {
81+
try_call!(raw::git_reference_name_is_valid(
82+
&mut valid,
83+
refname.as_ptr()
84+
));
85+
}
86+
Ok(valid == 1)
6687
}
6788

6889
/// Normalize reference name and check validity.
@@ -463,13 +484,23 @@ mod tests {
463484
use crate::{ObjectType, Reference, ReferenceType};
464485

465486
#[test]
466-
fn smoke() {
487+
fn is_valid_name() {
467488
assert!(Reference::is_valid_name("refs/foo"));
468489
assert!(!Reference::is_valid_name("foo"));
490+
assert!(Reference::is_valid_name("FOO_BAR"));
491+
492+
assert!(!Reference::is_valid_name("foo"));
493+
assert!(!Reference::is_valid_name("_FOO_BAR"));
469494
}
470495

471496
#[test]
472-
fn smoke2() {
497+
#[should_panic]
498+
fn is_valid_name_for_invalid_ref() {
499+
Reference::is_valid_name("ab\012");
500+
}
501+
502+
#[test]
503+
fn smoke() {
473504
let (_td, repo) = crate::test::repo_init();
474505
let mut head = repo.head().unwrap();
475506
assert!(head.is_branch());

src/remote.rs

+29-2
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,28 @@ impl<'repo> Remote<'repo> {
9292
pub fn is_valid_name(remote_name: &str) -> bool {
9393
crate::init();
9494
let remote_name = CString::new(remote_name).unwrap();
95-
unsafe { raw::git_remote_is_valid_name(remote_name.as_ptr()) == 1 }
95+
let mut valid: libc::c_int = 0;
96+
let return_code = unsafe {
97+
raw::git_remote_name_is_valid(
98+
&mut valid,
99+
remote_name.as_ptr()
100+
)
101+
};
102+
return_code >= 0 && valid == 1
103+
}
104+
105+
/// Ensure the remote name is well-formed.
106+
pub fn name_is_valid(remote_name: &str) -> Result<bool, Error> {
107+
crate::init();
108+
let remote_name = CString::new(remote_name)?;
109+
let mut valid: libc::c_int = 0;
110+
unsafe {
111+
try_call!(raw::git_remote_name_is_valid(
112+
&mut valid,
113+
remote_name.as_ptr()
114+
));
115+
}
116+
Ok(valid == 1)
96117
}
97118

98119
/// Create a detached remote
@@ -851,11 +872,17 @@ mod tests {
851872
}
852873

853874
#[test]
854-
fn is_valid() {
875+
fn is_valid_name() {
855876
assert!(Remote::is_valid_name("foobar"));
856877
assert!(!Remote::is_valid_name("\x01"));
857878
}
858879

880+
#[test]
881+
#[should_panic]
882+
fn is_valid_name_for_invalid_remote() {
883+
Remote::is_valid_name("ab\012");
884+
}
885+
859886
#[test]
860887
fn transfer_cb() {
861888
let (td, _repo) = crate::test::repo_init();

src/tag.rs

+37
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::ffi::CString;
12
use std::marker;
23
use std::mem;
34
use std::ptr;
@@ -15,6 +16,19 @@ pub struct Tag<'repo> {
1516
}
1617

1718
impl<'repo> Tag<'repo> {
19+
/// Determine whether a tag name is valid, meaning that (when prefixed with refs/tags/) that
20+
/// it is a valid reference name, and that any additional tag name restrictions are imposed
21+
/// (eg, it cannot start with a -).
22+
pub fn name_is_valid(tag_name: &str) -> Result<bool, Error> {
23+
crate::init();
24+
let tag_name = CString::new(tag_name)?;
25+
let mut valid: libc::c_int = 0;
26+
unsafe {
27+
try_call!(raw::git_tag_name_is_valid(&mut valid, tag_name.as_ptr()));
28+
}
29+
Ok(valid == 1)
30+
}
31+
1832
/// Get the id (SHA1) of a repository tag
1933
pub fn id(&self) -> Oid {
2034
unsafe { Binding::from_raw(raw::git_tag_id(&*self.raw)) }
@@ -141,6 +155,29 @@ impl<'repo> Drop for Tag<'repo> {
141155

142156
#[cfg(test)]
143157
mod tests {
158+
use crate::Tag;
159+
160+
// Reference -- https://git-scm.com/docs/git-check-ref-format
161+
#[test]
162+
fn name_is_valid() {
163+
assert_eq!(Tag::name_is_valid("blah_blah").unwrap(), true);
164+
assert_eq!(Tag::name_is_valid("v1.2.3").unwrap(), true);
165+
assert_eq!(Tag::name_is_valid("my/tag").unwrap(), true);
166+
assert_eq!(Tag::name_is_valid("@").unwrap(), true);
167+
168+
assert_eq!(Tag::name_is_valid("-foo").unwrap(), false);
169+
assert_eq!(Tag::name_is_valid("foo:bar").unwrap(), false);
170+
assert_eq!(Tag::name_is_valid("foo^bar").unwrap(), false);
171+
assert_eq!(Tag::name_is_valid("foo.").unwrap(), false);
172+
assert_eq!(Tag::name_is_valid("@{").unwrap(), false);
173+
assert_eq!(Tag::name_is_valid("as\\cd").unwrap(), false);
174+
175+
assert_eq!(
176+
Tag::name_is_valid("ab\012").err().unwrap().to_string(),
177+
"data contained a nul byte that could not be represented as a string"
178+
);
179+
}
180+
144181
#[test]
145182
fn smoke() {
146183
let (_td, repo) = crate::test::repo_init();

0 commit comments

Comments
 (0)