diff --git a/examples/cat-file.rs b/examples/cat-file.rs index 6196b9bb9f..7c6da33a0e 100644 --- a/examples/cat-file.rs +++ b/examples/cat-file.rs @@ -89,7 +89,7 @@ fn show_commit(commit: &Commit) { } show_sig("author", Some(commit.author())); show_sig("committer", Some(commit.committer())); - if let Some(msg) = commit.message() { + if let Ok(msg) = commit.message() { println!("\n{}", msg); } } @@ -100,7 +100,7 @@ fn show_tag(tag: &Tag) { println!("tag {}", tag.name().unwrap()); show_sig("tagger", tag.tagger()); - if let Some(msg) = tag.message() { + if let Ok(Some(msg)) = tag.message() { println!("\n{}", msg); } } diff --git a/examples/log.rs b/examples/log.rs index 5f6fe0f210..22c7a5d785 100644 --- a/examples/log.rs +++ b/examples/log.rs @@ -176,7 +176,7 @@ fn run(args: &Args) -> Result<(), Error> { if !sig_matches(&commit.committer(), &args.flag_committer) { return None; } - if !log_message_matches(commit.message(), &args.flag_grep) { + if !log_message_matches(commit.message().ok(), &args.flag_grep) { return None; } Some(Ok(commit)) diff --git a/examples/pull.rs b/examples/pull.rs index 27f461e546..ac1b4b7e0e 100644 --- a/examples/pull.rs +++ b/examples/pull.rs @@ -56,7 +56,7 @@ fn do_fetch<'a>( // Always fetch all tags. // Perform a download and also update tips fo.download_tags(git2::AutotagOption::All); - println!("Fetching {} for repo", remote.name().unwrap()); + println!("Fetching {} for repo", remote.name().unwrap().unwrap()); remote.fetch(refs, Some(&mut fo), None)?; // If there are local objects (we got a thin pack), then tell the user @@ -90,8 +90,8 @@ fn fast_forward( rc: &git2::AnnotatedCommit, ) -> Result<(), git2::Error> { let name = match lb.name() { - Some(s) => s.to_string(), - None => String::from_utf8_lossy(lb.name_bytes()).to_string(), + Ok(s) => s.to_string(), + Err(_) => String::from_utf8_lossy(lb.name_bytes()).to_string(), }; let msg = format!("Fast-Forward: Setting {} to id: {}", name, rc.id()); println!("{}", msg); diff --git a/examples/status.rs b/examples/status.rs index 0ed61711c8..f64f0b38bd 100644 --- a/examples/status.rs +++ b/examples/status.rs @@ -134,7 +134,7 @@ fn show_branch(repo: &Repository, format: &Format) -> Result<(), Error> { } Err(e) => return Err(e), }; - let head = head.as_ref().and_then(|h| h.shorthand()); + let head = head.as_ref().and_then(|h| h.shorthand().ok()); if format == &Format::Long { println!( diff --git a/examples/tag.rs b/examples/tag.rs index 5d2af02063..921863bee3 100644 --- a/examples/tag.rs +++ b/examples/tag.rs @@ -65,7 +65,7 @@ fn run(args: &Args) -> Result<(), Error> { } else if args.flag_list { let pattern = args.arg_pattern.as_ref().map(|s| &s[..]).unwrap_or("*"); for name in repo.tag_names(Some(pattern))?.iter() { - let name = name.unwrap(); + let name = name.expect("Not invalid utf8").expect("Not None"); let obj = repo.revparse_single(name)?; if let Some(tag) = obj.as_tag() { @@ -83,7 +83,7 @@ fn run(args: &Args) -> Result<(), Error> { fn print_tag(tag: &Tag, args: &Args) { print!("{:<16}", tag.name().unwrap()); if args.flag_n.is_some() { - print_list_lines(tag.message(), args); + print_list_lines(tag.message().unwrap(), args); } else { println!(); } @@ -92,7 +92,7 @@ fn print_tag(tag: &Tag, args: &Args) { fn print_commit(commit: &Commit, name: &str, args: &Args) { print!("{:<16}", name); if args.flag_n.is_some() { - print_list_lines(commit.message(), args); + print_list_lines(commit.message().ok(), args); } else { println!(); } diff --git a/src/blame.rs b/src/blame.rs index 13f3294191..0034127495 100644 --- a/src/blame.rs +++ b/src/blame.rs @@ -186,10 +186,12 @@ impl<'blame> BlameHunk<'blame> { /// The returned message is the summary of the commit, comprising the first /// paragraph of the message with whitespace trimmed and squashed. /// - /// `None` may be returned if an error occurs or if the summary is not valid - /// utf-8. - pub fn summary(&self) -> Option<&str> { - self.summary_bytes().and_then(|s| str::from_utf8(s).ok()) + /// `Ok(None)` may be returned if there is no summary. + pub fn summary(&self) -> Result, Error> { + match self.summary_bytes() { + Some(sb) => str::from_utf8(sb).map(|s| Some(s)).map_err(|e| e.into()), + None => Ok(None), + } } /// Get the short "summary" of the git commit message for the hunk. @@ -422,7 +424,7 @@ mod tests { assert_eq!(hunk.final_start_line(), 1); assert_eq!(hunk.path(), Some(Path::new("foo/bar"))); assert_eq!(hunk.lines_in_hunk(), 0); - assert_eq!(hunk.summary(), Some("commit")); + assert_eq!(hunk.summary(), Ok(Some("commit"))); assert!(!hunk.is_boundary()); let blame_buffer = blame.blame_buffer("\n".as_bytes()).unwrap(); diff --git a/src/buf.rs b/src/buf.rs index 7b714dfe9c..4e40cc91bb 100644 --- a/src/buf.rs +++ b/src/buf.rs @@ -5,6 +5,7 @@ use std::str; use crate::raw; use crate::util::Binding; +use crate::Error; /// A structure to wrap an intermediate buffer used by libgit2. /// @@ -34,10 +35,8 @@ impl Buf { } /// Attempt to view this buffer as a string slice. - /// - /// Returns `None` if the buffer is not valid utf-8. - pub fn as_str(&self) -> Option<&str> { - str::from_utf8(&**self).ok() + pub fn as_str(&self) -> Result<&str, Error> { + str::from_utf8(&**self).map_err(|e| e.into()) } } diff --git a/src/commit.rs b/src/commit.rs index ac1f6c1f47..c1fe337dbe 100644 --- a/src/commit.rs +++ b/src/commit.rs @@ -63,10 +63,8 @@ impl<'repo> Commit<'repo> { /// /// The returned message will be slightly prettified by removing any /// potential leading newlines. - /// - /// `None` will be returned if the message is not valid utf-8 - pub fn message(&self) -> Option<&str> { - str::from_utf8(self.message_bytes()).ok() + pub fn message(&self) -> Result<&str, Error> { + str::from_utf8(self.message_bytes()).map_err(|e| e.into()) } /// Get the full message of a commit as a byte slice. @@ -80,17 +78,18 @@ impl<'repo> Commit<'repo> { /// Get the encoding for the message of a commit, as a string representing a /// standard encoding name. /// - /// `None` will be returned if the encoding is not known - pub fn message_encoding(&self) -> Option<&str> { + /// `Ok(None)` will be returned if the encoding is not known + pub fn message_encoding(&self) -> Result, Error> { let bytes = unsafe { crate::opt_bytes(self, raw::git_commit_message_encoding(&*self.raw)) }; - bytes.and_then(|b| str::from_utf8(b).ok()) + match bytes { + Some(b) => str::from_utf8(b).map(|s| Some(s)).map_err(|e| e.into()), + None => Ok(None), + } } /// Get the full raw message of a commit. - /// - /// `None` will be returned if the message is not valid utf-8 - pub fn message_raw(&self) -> Option<&str> { - str::from_utf8(self.message_raw_bytes()).ok() + pub fn message_raw(&self) -> Result<&str, Error> { + str::from_utf8(self.message_raw_bytes()).map_err(|e| e.into()) } /// Get the full raw message of a commit. @@ -99,10 +98,8 @@ impl<'repo> Commit<'repo> { } /// Get the full raw text of the commit header. - /// - /// `None` will be returned if the message is not valid utf-8 - pub fn raw_header(&self) -> Option<&str> { - str::from_utf8(self.raw_header_bytes()).ok() + pub fn raw_header(&self) -> Result<&str, Error> { + str::from_utf8(self.raw_header_bytes()).map_err(|e| e.into()) } /// Get an arbitrary header field. @@ -129,10 +126,12 @@ impl<'repo> Commit<'repo> { /// The returned message is the summary of the commit, comprising the first /// paragraph of the message with whitespace trimmed and squashed. /// - /// `None` may be returned if an error occurs or if the summary is not valid - /// utf-8. - pub fn summary(&self) -> Option<&str> { - self.summary_bytes().and_then(|s| str::from_utf8(s).ok()) + /// `Ok(None)` may be returned if there is no summary + pub fn summary(&self) -> Result, Error> { + match self.summary_bytes() { + Some(sb) => str::from_utf8(sb).map(|s| Some(s)).map_err(|e| e.into()), + None => Ok(None), + } } /// Get the short "summary" of the git commit message. @@ -151,10 +150,12 @@ impl<'repo> Commit<'repo> { /// but the first paragraph of the message. Leading and trailing whitespaces /// are trimmed. /// - /// `None` may be returned if an error occurs or if the summary is not valid - /// utf-8. - pub fn body(&self) -> Option<&str> { - self.body_bytes().and_then(|s| str::from_utf8(s).ok()) + /// `Ok(None)` may be returned if there is no body. + pub fn body(&self) -> Result, Error> { + match self.body_bytes() { + Some(sb) => str::from_utf8(sb).map(|s| Some(s)).map_err(|e| e.into()), + None => Ok(None), + } } /// Get the long "body" of the git commit message. @@ -347,7 +348,7 @@ impl<'repo> std::fmt::Debug for Commit<'repo> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { let mut ds = f.debug_struct("Commit"); ds.field("id", &self.id()); - if let Some(summary) = self.summary() { + if let Ok(Some(summary)) = self.summary() { ds.field("summary", &summary); } ds.finish() @@ -424,12 +425,12 @@ mod tests { let head = repo.head().unwrap(); let target = head.target().unwrap(); let commit = repo.find_commit(target).unwrap(); - assert_eq!(commit.message(), Some("initial\n\nbody")); - assert_eq!(commit.body(), Some("body")); + assert_eq!(commit.message(), Ok("initial\n\nbody")); + assert_eq!(commit.body(), Ok(Some("body"))); assert_eq!(commit.id(), target); commit.message_raw().unwrap(); commit.raw_header().unwrap(); - commit.message_encoding(); + commit.message_encoding().expect("Should not be invalid"); commit.summary().unwrap(); commit.body().unwrap(); commit.tree_id(); @@ -441,10 +442,10 @@ mod tests { crate::Oid::from_str(tree_header_bytes.as_str().unwrap()).unwrap(), commit.tree_id() ); - assert_eq!(commit.author().name(), Some("name")); - assert_eq!(commit.author().email(), Some("email")); - assert_eq!(commit.committer().name(), Some("name")); - assert_eq!(commit.committer().email(), Some("email")); + assert_eq!(commit.author().name(), Ok("name")); + assert_eq!(commit.author().email(), Ok("email")); + assert_eq!(commit.committer().name(), Ok("name")); + assert_eq!(commit.committer().email(), Ok("email")); let sig = repo.signature().unwrap(); let tree = repo.find_tree(commit.tree_id()).unwrap(); @@ -457,7 +458,7 @@ mod tests { .amend(Some("HEAD"), None, None, None, Some("new message"), None) .unwrap(); let new_head = repo.find_commit(new_head).unwrap(); - assert_eq!(new_head.message(), Some("new message")); + assert_eq!(new_head.message(), Ok("new message")); new_head.into_object(); repo.find_object(target, None).unwrap().as_commit().unwrap(); diff --git a/src/config.rs b/src/config.rs index 9ba0a6da64..b2b51541d1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -522,10 +522,8 @@ impl Drop for Config { impl<'cfg> ConfigEntry<'cfg> { /// Gets the name of this entry. - /// - /// May return `None` if the name is not valid utf-8 - pub fn name(&self) -> Option<&str> { - str::from_utf8(self.name_bytes()).ok() + pub fn name(&self) -> Result<&str, Error> { + str::from_utf8(self.name_bytes()).map_err(|e| e.into()) } /// Gets the name of this entry as a byte slice. @@ -535,13 +533,11 @@ impl<'cfg> ConfigEntry<'cfg> { /// Gets the value of this entry. /// - /// May return `None` if the value is not valid utf-8 - /// /// # Panics /// /// Panics when no value is defined. - pub fn value(&self) -> Option<&str> { - str::from_utf8(self.value_bytes()).ok() + pub fn value(&self) -> Result<&str, Error> { + str::from_utf8(self.value_bytes()).map_err(|e| e.into()) } /// Gets the value of this entry as a byte slice. @@ -683,8 +679,8 @@ mod tests { let mut entries = cfg.entries(None).unwrap(); while let Some(entry) = entries.next() { let entry = entry.unwrap(); - entry.name(); - entry.value(); + entry.name().unwrap(); + entry.value().unwrap(); entry.level(); } } diff --git a/src/mailmap.rs b/src/mailmap.rs index 096b3227c7..384406a84e 100644 --- a/src/mailmap.rs +++ b/src/mailmap.rs @@ -105,8 +105,8 @@ mod tests { let mut mm = t!(Mailmap::new()); let mailmapped_sig = t!(mm.resolve_signature(&sig)); - assert_eq!(mailmapped_sig.name(), Some(sig_name)); - assert_eq!(mailmapped_sig.email(), Some(sig_email)); + assert_eq!(mailmapped_sig.name(), Ok(sig_name)); + assert_eq!(mailmapped_sig.email(), Ok(sig_email)); t!(mm.add_entry(None, None, None, sig_email)); t!(mm.add_entry( @@ -117,8 +117,8 @@ mod tests { )); let mailmapped_sig = t!(mm.resolve_signature(&sig)); - assert_eq!(mailmapped_sig.name(), Some("real name")); - assert_eq!(mailmapped_sig.email(), Some("real@email")); + assert_eq!(mailmapped_sig.name(), Ok("real name")); + assert_eq!(mailmapped_sig.email(), Ok("real@email")); } #[test] @@ -128,7 +128,7 @@ mod tests { let sig = t!(Signature::now("name", "email")); let mailmapped_sig = t!(mm.resolve_signature(&sig)); - assert_eq!(mailmapped_sig.name(), Some("name")); - assert_eq!(mailmapped_sig.email(), Some("prøper@emæil")); + assert_eq!(mailmapped_sig.name(), Ok("name")); + assert_eq!(mailmapped_sig.email(), Ok("prøper@emæil")); } } diff --git a/src/merge.rs b/src/merge.rs index 4b3cadce74..76a1151dfb 100644 --- a/src/merge.rs +++ b/src/merge.rs @@ -44,10 +44,8 @@ impl<'repo> AnnotatedCommit<'repo> { } /// Get the refname that the given git_annotated_commit refers to - /// - /// Returns None if it is not valid utf8 - pub fn refname(&self) -> Option<&str> { - str::from_utf8(self.refname_bytes()).ok() + pub fn refname(&self) -> Result<&str, Error> { + str::from_utf8(self.refname_bytes()).map_err(|e| e.into()) } /// Get the refname that the given git_annotated_commit refers to. @@ -361,11 +359,12 @@ impl MergeFileResult { /// The path that the resultant merge file should use. /// - /// returns `None` if a filename conflict would occur, - /// or if the path is not valid utf-8 - pub fn path(&self) -> Option<&str> { - self.path_bytes() - .and_then(|bytes| str::from_utf8(bytes).ok()) + /// returns `Ok(None)` if a filename conflict would occur + pub fn path(&self) -> Result, Error> { + match self.path_bytes() { + Some(pb) => str::from_utf8(pb).map(|s| Some(s)).map_err(|e| e.into()), + None => Ok(None), + } } /// Gets the path as a byte slice. @@ -403,7 +402,7 @@ impl Drop for MergeFileResult { impl std::fmt::Debug for MergeFileResult { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut ds = f.debug_struct("MergeFileResult"); - if let Some(path) = &self.path() { + if let Ok(Some(path)) = &self.path() { ds.field("path", path); } ds.field("automergeable", &self.is_automergeable()); @@ -541,7 +540,7 @@ mod tests { let merge_file_result = crate::merge_file(&base, &ours, &theirs, Some(&mut opts)).unwrap(); assert!(!merge_file_result.is_automergeable()); - assert_eq!(merge_file_result.path(), Some("file")); + assert_eq!(merge_file_result.path(), Ok(Some("file"))); assert_eq!( String::from_utf8_lossy(merge_file_result.content()).to_string(), r"<<<<<<< ours diff --git a/src/note.rs b/src/note.rs index 821a7baf76..d6174dcfba 100644 --- a/src/note.rs +++ b/src/note.rs @@ -38,9 +38,9 @@ impl<'repo> Note<'repo> { unsafe { crate::opt_bytes(self, raw::git_note_message(&*self.raw)).unwrap() } } - /// Get the note message as a string, returning `None` if it is not UTF-8. - pub fn message(&self) -> Option<&str> { - str::from_utf8(self.message_bytes()).ok() + /// Get the note message as a string. + pub fn message(&self) -> Result<&str, Error> { + str::from_utf8(self.message_bytes()).map_err(|e| e.into()) } /// Get the note object's id @@ -130,7 +130,7 @@ mod tests { let note_obj = repo.find_note(None, head).unwrap(); assert_eq!(note_obj.id(), note); - assert_eq!(note_obj.message(), Some("foo")); + assert_eq!(note_obj.message(), Ok("foo")); let (a, b) = repo.notes(None).unwrap().next().unwrap().unwrap(); assert_eq!(a, note); diff --git a/src/packbuilder.rs b/src/packbuilder.rs index de47bbce32..06cfd6c4df 100644 --- a/src/packbuilder.rs +++ b/src/packbuilder.rs @@ -199,10 +199,12 @@ impl<'repo> PackBuilder<'repo> { /// The packfile's name is derived from the packfile's content. This is only /// correct after the packfile has been written. /// - /// Returns `None` if the packfile has not been written or if the name is - /// not valid utf-8. - pub fn name(&self) -> Option<&str> { - self.name_bytes().and_then(|s| str::from_utf8(s).ok()) + /// Returns `Ok(None)` if the packfile has not been written. + pub fn name(&self) -> Result, Error> { + match self.name_bytes() { + Some(nb) => str::from_utf8(nb).map(|s| Some(s)).map_err(|e| e.into()), + None => Ok(None), + } } /// Get the unique name for the resulting packfile, in bytes. @@ -336,7 +338,7 @@ mod tests { { assert!(builder.hash().unwrap().is_zero()); } - assert!(builder.name().is_none()); + assert!(builder.name().expect("Should return Ok result").is_none()); assert_eq!(&*buf, &*empty_pack_header()); } @@ -349,7 +351,7 @@ mod tests { { assert!(builder.hash().unwrap() == Oid::from_str(EMPTY_PACKFILE_OID).unwrap()); } - assert!(builder.name().unwrap() == EMPTY_PACKFILE_OID); + assert!(builder.name().unwrap() == Some(EMPTY_PACKFILE_OID)); } #[test] diff --git a/src/push_update.rs b/src/push_update.rs index 97bebb1921..7fdbb69dfc 100644 --- a/src/push_update.rs +++ b/src/push_update.rs @@ -1,5 +1,5 @@ use crate::util::Binding; -use crate::{raw, Oid}; +use crate::{raw, Error, Oid}; use std::marker; use std::str; @@ -28,9 +28,9 @@ impl PushUpdate<'_> { unsafe { crate::opt_bytes(self, (*self.raw).src_refname).unwrap() } } - /// Returns the source name of the reference, or None if it is not valid UTF-8. - pub fn src_refname(&self) -> Option<&str> { - str::from_utf8(self.src_refname_bytes()).ok() + /// Returns the source name of the reference. + pub fn src_refname(&self) -> Result<&str, Error> { + str::from_utf8(self.src_refname_bytes()).map_err(|e| e.into()) } /// Returns the name of the reference to update on the server as a byte slice. @@ -38,9 +38,9 @@ impl PushUpdate<'_> { unsafe { crate::opt_bytes(self, (*self.raw).dst_refname).unwrap() } } - /// Returns the name of the reference to update on the server, or None if it is not valid UTF-8. - pub fn dst_refname(&self) -> Option<&str> { - str::from_utf8(self.dst_refname_bytes()).ok() + /// Returns the name of the reference to update on the server. + pub fn dst_refname(&self) -> Result<&str, Error> { + str::from_utf8(self.dst_refname_bytes()).map_err(|e| e.into()) } /// Returns the current target of the reference. diff --git a/src/rebase.rs b/src/rebase.rs index 2bf8fe3e8a..1d88c0cd86 100644 --- a/src/rebase.rs +++ b/src/rebase.rs @@ -113,10 +113,13 @@ impl<'repo> Rebase<'repo> { } /// Gets the original `HEAD` ref name for merge rebases. - pub fn orig_head_name(&self) -> Option<&str> { + pub fn orig_head_name(&self) -> Result, Error> { let name_bytes = unsafe { crate::opt_bytes(self, raw::git_rebase_orig_head_name(self.raw)) }; - name_bytes.and_then(|s| str::from_utf8(s).ok()) + match name_bytes { + Some(nb) => str::from_utf8(nb).map(|s| Some(s)).map_err(|e| e.into()), + None => Ok(None), + } } /// Gets the original HEAD id for merge rebases. @@ -315,8 +318,12 @@ impl<'rebase> RebaseOperation<'rebase> { ///The executable the user has requested be run. This will only /// be populated for operations of type RebaseOperationType::Exec - pub fn exec(&self) -> Option<&str> { - unsafe { str::from_utf8(crate::opt_bytes(self, (*self.raw).exec).unwrap()).ok() } + pub fn exec(&self) -> Result, Error> { + let exec_bytes = unsafe { crate::opt_bytes(self, (*self.raw).exec) }; + match exec_bytes { + Some(eb) => str::from_utf8(eb).map(|s| Some(s)).map_err(|e| e.into()), + None => Ok(None), + } } } @@ -363,7 +370,7 @@ mod tests { .rebase(Some(&branch), Some(&upstream), None, None) .unwrap(); - assert_eq!(Some("refs/heads/main"), rebase.orig_head_name()); + assert_eq!(Ok(Some("refs/heads/main")), rebase.orig_head_name()); assert_eq!(Some(c2), rebase.orig_head_id()); assert_eq!(rebase.len(), 2); @@ -423,18 +430,18 @@ mod tests { rebase.next().unwrap().unwrap(); let id = rebase.commit(None, &sig, None).unwrap(); let commit = repo.find_commit(id).unwrap(); - assert_eq!(commit.message(), Some("A")); - assert_eq!(commit.author().name(), Some("testname")); - assert_eq!(commit.author().email(), Some("testemail")); + assert_eq!(commit.message(), Ok("A")); + assert_eq!(commit.author().name(), Ok("testname")); + assert_eq!(commit.author().email(), Ok("testemail")); } { rebase.next().unwrap().unwrap(); let id = rebase.commit(None, &sig, None).unwrap(); let commit = repo.find_commit(id).unwrap(); - assert_eq!(commit.message(), Some("B")); - assert_eq!(commit.author().name(), Some("testname")); - assert_eq!(commit.author().email(), Some("testemail")); + assert_eq!(commit.message(), Ok("B")); + assert_eq!(commit.author().name(), Ok("testname")); + assert_eq!(commit.author().email(), Ok("testemail")); } rebase.finish(None).unwrap(); } diff --git a/src/reference.rs b/src/reference.rs index c2a1d8f8b5..04ed5cc0e5 100644 --- a/src/reference.rs +++ b/src/reference.rs @@ -217,10 +217,8 @@ impl<'repo> Reference<'repo> { } /// Get the full name of a reference. - /// - /// Returns `None` if the name is not valid utf-8. - pub fn name(&self) -> Option<&str> { - str::from_utf8(self.name_bytes()).ok() + pub fn name(&self) -> Result<&str, Error> { + str::from_utf8(self.name_bytes()).map_err(|e| e.into()) } /// Get the full name of a reference. @@ -232,10 +230,8 @@ impl<'repo> Reference<'repo> { /// /// This will transform the reference name into a name "human-readable" /// version. If no shortname is appropriate, it will return the full name. - /// - /// Returns `None` if the shorthand is not valid utf-8. - pub fn shorthand(&self) -> Option<&str> { - str::from_utf8(self.shorthand_bytes()).ok() + pub fn shorthand(&self) -> Result<&str, Error> { + str::from_utf8(self.shorthand_bytes()).map_err(|e| e.into()) } /// Get the full shorthand of a reference. @@ -261,11 +257,12 @@ impl<'repo> Reference<'repo> { /// Get full name to the reference pointed to by a symbolic reference. /// - /// May return `None` if the reference is either not symbolic or not a - /// valid utf-8 string. - pub fn symbolic_target(&self) -> Option<&str> { - self.symbolic_target_bytes() - .and_then(|s| str::from_utf8(s).ok()) + /// May return `Ok(None)` if the reference is not symbolic. + pub fn symbolic_target(&self) -> Result, Error> { + match self.symbolic_target_bytes() { + Some(stb) => str::from_utf8(stb).map(|s| Some(s)).map_err(|e| e.into()), + None => Ok(None), + } } /// Get full name to the reference pointed to by a symbolic reference. @@ -549,7 +546,7 @@ mod tests { assert_eq!(head.kind().unwrap(), ReferenceType::Direct); assert!(head == repo.head().unwrap()); - assert_eq!(head.name(), Some("refs/heads/main")); + assert_eq!(head.name(), Ok("refs/heads/main")); assert!(head == repo.find_reference("refs/heads/main").unwrap()); assert_eq!( @@ -557,10 +554,13 @@ mod tests { head.target().unwrap() ); - assert!(head.symbolic_target().is_none()); + assert!(head + .symbolic_target() + .expect("Should be okay even if None") + .is_none()); assert!(head.target_peel().is_none()); - assert_eq!(head.shorthand(), Some("main")); + assert_eq!(head.shorthand(), Ok("main")); assert!(head.resolve().unwrap() == head); // In a block so that it gets dropped before proceeding and we can @@ -601,7 +601,7 @@ mod tests { .symbolic_set_target("refs/tags/tag1", "test") .unwrap(); assert_eq!(sym2.kind().unwrap(), ReferenceType::Symbolic); - assert_eq!(sym2.symbolic_target().unwrap(), "refs/tags/tag1"); + assert_eq!(sym2.symbolic_target().unwrap(), Some("refs/tags/tag1")); // In a block so that it gets dropped before proceeding and we can // confirm that `sym1` and `sym2` still work diff --git a/src/reflog.rs b/src/reflog.rs index bbd2140ab2..919234ba6f 100644 --- a/src/reflog.rs +++ b/src/reflog.rs @@ -136,9 +136,12 @@ impl<'reflog> ReflogEntry<'reflog> { unsafe { Binding::from_raw(raw::git_reflog_entry_id_old(self.raw)) } } - /// Get the log message, returning `None` on invalid UTF-8. - pub fn message(&self) -> Option<&str> { - self.message_bytes().and_then(|s| str::from_utf8(s).ok()) + /// Get the log message. + pub fn message(&self) -> Result, Error> { + match self.message_bytes() { + Some(mb) => str::from_utf8(mb).map(|s| Some(s)).map_err(|e| e.into()), + None => Ok(None), + } } /// Get the log message as a byte array. @@ -188,7 +191,7 @@ mod tests { reflog.write().unwrap(); let entry = reflog.iter().next().unwrap(); - assert!(entry.message().is_some()); + assert!(entry.message().expect("Should be ok").is_some()); repo.reflog_rename("HEAD", "refs/heads/foo").unwrap(); repo.reflog_delete("refs/heads/foo").unwrap(); diff --git a/src/refspec.rs b/src/refspec.rs index 3f62e991c7..fc28609647 100644 --- a/src/refspec.rs +++ b/src/refspec.rs @@ -26,10 +26,8 @@ impl<'remote> Refspec<'remote> { } /// Get the destination specifier. - /// - /// If the destination is not utf-8, None is returned. - pub fn dst(&self) -> Option<&str> { - str::from_utf8(self.dst_bytes()).ok() + pub fn dst(&self) -> Result<&str, Error> { + str::from_utf8(self.dst_bytes()).map_err(|e| e.into()) } /// Get the destination specifier, in bytes. @@ -44,10 +42,8 @@ impl<'remote> Refspec<'remote> { } /// Get the source specifier. - /// - /// If the source is not utf-8, None is returned. - pub fn src(&self) -> Option<&str> { - str::from_utf8(self.src_bytes()).ok() + pub fn src(&self) -> Result<&str, Error> { + str::from_utf8(self.src_bytes()).map_err(|e| e.into()) } /// Get the source specifier, in bytes. @@ -67,10 +63,8 @@ impl<'remote> Refspec<'remote> { } /// Get the refspec's string. - /// - /// Returns None if the string is not valid utf8. - pub fn str(&self) -> Option<&str> { - str::from_utf8(self.bytes()).ok() + pub fn str(&self) -> Result<&str, Error> { + str::from_utf8(self.bytes()).map_err(|e| e.into()) } /// Get the refspec's string as a byte array diff --git a/src/remote.rs b/src/remote.rs index 0c13a53fcf..3358f3793d 100644 --- a/src/remote.rs +++ b/src/remote.rs @@ -125,10 +125,12 @@ impl<'repo> Remote<'repo> { /// Get the remote's name. /// - /// Returns `None` if this remote has not yet been named or if the name is - /// not valid utf-8 - pub fn name(&self) -> Option<&str> { - self.name_bytes().and_then(|s| str::from_utf8(s).ok()) + /// Returns `Ok(None)` if this remote has not yet been named. + pub fn name(&self) -> Result, Error> { + match self.name_bytes() { + Some(nb) => str::from_utf8(nb).map(|s| Some(s)).map_err(|e| e.into()), + None => Ok(None), + } } /// Get the remote's name, in bytes. @@ -139,10 +141,8 @@ impl<'repo> Remote<'repo> { } /// Get the remote's URL. - /// - /// Returns `None` if the URL is not valid utf-8 - pub fn url(&self) -> Option<&str> { - str::from_utf8(self.url_bytes()).ok() + pub fn url(&self) -> Result<&str, Error> { + str::from_utf8(self.url_bytes()).map_err(|e| e.into()) } /// Get the remote's URL as a byte array. @@ -152,9 +152,12 @@ impl<'repo> Remote<'repo> { /// Get the remote's pushurl. /// - /// Returns `None` if the pushurl is not valid utf-8 or no special url for pushing is set. - pub fn pushurl(&self) -> Option<&str> { - self.pushurl_bytes().and_then(|s| str::from_utf8(s).ok()) + /// Returns `Ok(None)` if no special url for pushing is set. + pub fn pushurl(&self) -> Result, Error> { + match self.pushurl_bytes() { + Some(pb) => str::from_utf8(pb).map(|s| Some(s)).map_err(|e| e.into()), + None => Ok(None), + } } /// Get the remote's pushurl as a byte array. @@ -807,9 +810,9 @@ mod tests { let repo = t!(Repository::init(td.path())); let mut origin = t!(repo.find_remote("origin")); - assert_eq!(origin.name(), Some("origin")); - assert_eq!(origin.url(), Some("/path/to/nowhere")); - assert_eq!(origin.pushurl(), None); + assert_eq!(origin.name(), Ok(Some("origin"))); + assert_eq!(origin.url(), Ok("/path/to/nowhere")); + assert_eq!(origin.pushurl(), Ok(None)); t!(repo.remote_set_url("origin", "/path/to/elsewhere")); t!(repo.remote_set_pushurl("origin", Some("/path/to/elsewhere"))); @@ -837,26 +840,26 @@ mod tests { }; let mut origin = repo.remote("origin", &url).unwrap(); - assert_eq!(origin.name(), Some("origin")); - assert_eq!(origin.url(), Some(&url[..])); - assert_eq!(origin.pushurl(), None); + assert_eq!(origin.name(), Ok(Some("origin"))); + assert_eq!(origin.url(), Ok(&url[..])); + assert_eq!(origin.pushurl(), Ok(None)); { let mut specs = origin.refspecs(); let spec = specs.next().unwrap(); assert!(specs.next().is_none()); - assert_eq!(spec.str(), Some("+refs/heads/*:refs/remotes/origin/*")); - assert_eq!(spec.dst(), Some("refs/remotes/origin/*")); - assert_eq!(spec.src(), Some("refs/heads/*")); + assert_eq!(spec.str(), Ok("+refs/heads/*:refs/remotes/origin/*")); + assert_eq!(spec.dst(), Ok("refs/remotes/origin/*")); + assert_eq!(spec.src(), Ok("refs/heads/*")); assert!(spec.is_force()); } assert!(origin.refspecs().next_back().is_some()); { let remotes = repo.remotes().unwrap(); assert_eq!(remotes.len(), 1); - assert_eq!(remotes.get(0), Some("origin")); + assert_eq!(remotes.get(0), Ok(Some("origin"))); assert_eq!(remotes.iter().count(), 1); - assert_eq!(remotes.iter().next().unwrap(), Some("origin")); + assert_eq!(remotes.iter().next().unwrap(), Ok(Some("origin"))); } origin.connect(Direction::Push).unwrap(); @@ -917,7 +920,7 @@ mod tests { let repo = Repository::init(td.path()).unwrap(); let origin = repo.remote_anonymous("/path/to/nowhere").unwrap(); - assert_eq!(origin.name(), None); + assert_eq!(origin.name(), Ok(None)); drop(origin.clone()); } @@ -1033,7 +1036,7 @@ mod tests { let repo = Repository::clone(&url, td3.path()).unwrap(); let commit = repo.head().unwrap().target().unwrap(); let commit = repo.find_commit(commit).unwrap(); - assert_eq!(commit.message(), Some("initial\n\nbody")); + assert_eq!(commit.message(), Ok("initial\n\nbody")); } #[test] diff --git a/src/repo.rs b/src/repo.rs index 5c51a302dc..ded4ecc8b9 100644 --- a/src/repo.rs +++ b/src/repo.rs @@ -539,10 +539,12 @@ impl Repository { /// Get the currently active namespace for this repository. /// - /// If there is no namespace, or the namespace is not a valid utf8 string, - /// `None` is returned. - pub fn namespace(&self) -> Option<&str> { - self.namespace_bytes().and_then(|s| str::from_utf8(s).ok()) + /// If there is no namespace, Ok(None) is returned. + pub fn namespace(&self) -> Result, Error> { + match self.namespace_bytes() { + Some(nb) => str::from_utf8(nb).map(|s| Some(s)).map_err(|e| e.into()), + None => Ok(None), + } } /// Get the currently active namespace for this repository as a byte array. @@ -3671,7 +3673,7 @@ mod tests { let repo = Repository::init_bare(path).unwrap(); assert!(repo.is_bare()); - assert!(repo.namespace().is_none()); + assert!(repo.namespace().expect("Okay even if none").is_none()); assert_eq!(repo.object_format(), ObjectFormat::Sha1); } @@ -4239,7 +4241,7 @@ mod tests { .unwrap(); assert!(!merge_file_result.is_automergeable()); - assert_eq!(merge_file_result.path(), Some("file")); + assert_eq!(merge_file_result.path(), Ok(Some("file"))); assert_eq!( String::from_utf8_lossy(merge_file_result.content()).to_string(), r"<<<<<<< ours @@ -4509,11 +4511,14 @@ bar // url repo2.submodule_set_url(name, "fake-url")?; - assert_eq!(repo2.find_submodule(name)?.url(), Some("fake-url")); + assert_eq!(repo2.find_submodule(name)?.url(), Ok(Some("fake-url"))); // branch repo2.submodule_set_branch(name, "fake-branch")?; - assert_eq!(repo2.find_submodule(name)?.branch(), Some("fake-branch")); + assert_eq!( + repo2.find_submodule(name)?.branch(), + Ok(Some("fake-branch")) + ); Ok(()) } @@ -4530,10 +4535,10 @@ bar // This is our baseline for HEAD. let author = commit.author(); let committer = commit.committer(); - assert_eq!(author.name(), Some("name")); - assert_eq!(author.email(), Some("email")); - assert_eq!(committer.name(), Some("name")); - assert_eq!(committer.email(), Some("email")); + assert_eq!(author.name(), Ok("name")); + assert_eq!(author.email(), Ok("email")); + assert_eq!(committer.name(), Ok("name")); + assert_eq!(committer.email(), Ok("email")); // There is no .mailmap file in the test repo so all signature identities are equal. let mailmap = t!(repo.mailmap()); @@ -4579,10 +4584,10 @@ Committer Name "#, let committer = commit.committer(); assert_ne!(author.name(), committer.name()); assert_ne!(author.email(), committer.email()); - assert_eq!(author.name(), Some("name")); - assert_eq!(author.email(), Some("email")); - assert_eq!(committer.name(), Some("committer")); - assert_eq!(committer.email(), Some("committer@email")); + assert_eq!(author.name(), Ok("name")); + assert_eq!(author.email(), Ok("email")); + assert_eq!(committer.name(), Ok("committer")); + assert_eq!(committer.email(), Ok("committer@email")); // Fetch the newly added .mailmap from the repository. let mailmap = t!(repo.mailmap()); @@ -4598,10 +4603,10 @@ Committer Name "#, drop(commit); // author_with_mailmap() + committer_with_mailmap() work - assert_eq!(mailmapped_author.name(), Some("Author Name")); - assert_eq!(mailmapped_author.email(), Some("author.proper@email")); - assert_eq!(mailmapped_committer.name(), Some("Committer Name")); - assert_eq!(mailmapped_committer.email(), Some("committer.proper@email")); + assert_eq!(mailmapped_author.name(), Ok("Author Name")); + assert_eq!(mailmapped_author.email(), Ok("author.proper@email")); + assert_eq!(mailmapped_committer.name(), Ok("Committer Name")); + assert_eq!(mailmapped_committer.email(), Ok("committer.proper@email")); // resolve_signature() works assert_eq!(mm_resolve_author.email(), mailmapped_author.email()); @@ -4731,14 +4736,14 @@ Committer Name "#, macro_rules! expect { ($name:literal, $email:literal, ==) => { let sig = repo.author_from_env().unwrap(); - assert_eq!(Some($name), sig.name()); - assert_eq!(Some($email), sig.email()); + assert_eq!(Ok($name), sig.name()); + assert_eq!(Ok($email), sig.email()); assert_eq!(time, sig.when()); }; ($name:literal, $email:literal, !=) => { let sig = repo.author_from_env().unwrap(); - assert_eq!(Some($name), sig.name()); - assert_eq!(Some($email), sig.email()); + assert_eq!(Ok($name), sig.name()); + assert_eq!(Ok($email), sig.email()); assert_ne!(time, sig.when()); }; } @@ -4783,14 +4788,14 @@ Committer Name "#, macro_rules! expect { ($name:literal, $email:literal, ==) => { let sig = repo.committer_from_env().unwrap(); - assert_eq!(Some($name), sig.name()); - assert_eq!(Some($email), sig.email()); + assert_eq!(Ok($name), sig.name()); + assert_eq!(Ok($email), sig.email()); assert_eq!(time, sig.when()); }; ($name:literal, $email:literal, !=) => { let sig = repo.committer_from_env().unwrap(); - assert_eq!(Some($name), sig.name()); - assert_eq!(Some($email), sig.email()); + assert_eq!(Ok($name), sig.name()); + assert_eq!(Ok($email), sig.email()); assert_ne!(time, sig.when()); }; } diff --git a/src/signature.rs b/src/signature.rs index 074ce31f6d..362a6f4465 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -62,10 +62,8 @@ impl<'a> Signature<'a> { } /// Gets the name on the signature. - /// - /// Returns `None` if the name is not valid utf-8 - pub fn name(&self) -> Option<&str> { - str::from_utf8(self.name_bytes()).ok() + pub fn name(&self) -> Result<&str, Error> { + str::from_utf8(self.name_bytes()).map_err(|e| e.into()) } /// Gets the name on the signature as a byte slice. @@ -74,10 +72,8 @@ impl<'a> Signature<'a> { } /// Gets the email on the signature. - /// - /// Returns `None` if the email is not valid utf-8 - pub fn email(&self) -> Option<&str> { - str::from_utf8(self.email_bytes()).ok() + pub fn email(&self) -> Result<&str, Error> { + str::from_utf8(self.email_bytes()).map_err(|e| e.into()) } /// Gets the email on the signature as a byte slice. @@ -179,8 +175,8 @@ mod tests { assert!(Signature::now("", "bar").is_err()); let s = Signature::now("foo", "bar").unwrap(); - assert_eq!(s.name(), Some("foo")); - assert_eq!(s.email(), Some("bar")); + assert_eq!(s.name(), Ok("foo")); + assert_eq!(s.email(), Ok("bar")); drop(s.clone()); drop(s.to_owned()); diff --git a/src/status.rs b/src/status.rs index a679a688c7..89a22e190a 100644 --- a/src/status.rs +++ b/src/status.rs @@ -7,7 +7,7 @@ use std::ops::Range; use std::str; use crate::util::{self, Binding}; -use crate::{raw, DiffDelta, IntoCString, Repository, Status}; +use crate::{raw, DiffDelta, Error, IntoCString, Repository, Status}; /// Options that can be provided to `repo.statuses()` to control how the status /// information is gathered. @@ -329,10 +329,8 @@ impl<'statuses> StatusEntry<'statuses> { } /// Access this entry's path name as a string. - /// - /// Returns `None` if the path is not valid utf-8. - pub fn path(&self) -> Option<&str> { - str::from_utf8(self.path_bytes()).ok() + pub fn path(&self) -> Result<&str, Error> { + str::from_utf8(self.path_bytes()).map_err(|e| e.into()) } /// Access the status flags for this file @@ -382,7 +380,7 @@ mod tests { let statuses = repo.statuses(None).unwrap(); assert_eq!(statuses.iter().count(), 1); let status = statuses.iter().next().unwrap(); - assert_eq!(status.path(), Some("foo")); + assert_eq!(status.path(), Ok("foo")); assert!(status.status().contains(crate::Status::WT_NEW)); assert!(!status.status().contains(crate::Status::INDEX_NEW)); assert!(status.head_to_index().is_none()); @@ -402,7 +400,7 @@ mod tests { let statuses = t!(repo.statuses(Some(&mut opts))); assert_eq!(statuses.iter().count(), 1); let status = statuses.iter().next().unwrap(); - assert_eq!(status.path(), Some("foo")); + assert_eq!(status.path(), Ok("foo")); } #[test] diff --git a/src/string_array.rs b/src/string_array.rs index c77ccdab96..8d68616cfc 100644 --- a/src/string_array.rs +++ b/src/string_array.rs @@ -6,6 +6,7 @@ use std::str; use crate::raw; use crate::util::Binding; +use crate::Error; /// A string array structure used by libgit2 /// @@ -29,9 +30,12 @@ pub struct IterBytes<'a> { } impl StringArray { - /// Returns None if the i'th string is not utf8 or if i is out of bounds. - pub fn get(&self, i: usize) -> Option<&str> { - self.get_bytes(i).and_then(|s| str::from_utf8(s).ok()) + /// Returns Ok(None) if i is out of bounds. + pub fn get(&self, i: usize) -> Result, Error> { + match self.get_bytes(i) { + Some(gb) => str::from_utf8(gb).map(|s| Some(s)).map_err(|e| e.into()), + None => Ok(None), + } } /// Returns None if `i` is out of bounds. @@ -88,7 +92,7 @@ impl Binding for StringArray { } impl<'a> IntoIterator for &'a StringArray { - type Item = Option<&'a str>; + type Item = Result, Error>; type IntoIter = Iter<'a>; fn into_iter(self) -> Self::IntoIter { self.iter() @@ -96,8 +100,8 @@ impl<'a> IntoIterator for &'a StringArray { } impl<'a> Iterator for Iter<'a> { - type Item = Option<&'a str>; - fn next(&mut self) -> Option> { + type Item = Result, Error>; + fn next(&mut self) -> Option, Error>> { self.range.next().map(|i| self.arr.get(i)) } fn size_hint(&self) -> (usize, Option) { @@ -105,7 +109,7 @@ impl<'a> Iterator for Iter<'a> { } } impl<'a> DoubleEndedIterator for Iter<'a> { - fn next_back(&mut self) -> Option> { + fn next_back(&mut self) -> Option, Error>> { self.range.next_back().map(|i| self.arr.get(i)) } } diff --git a/src/submodule.rs b/src/submodule.rs index 06a6359400..e38d6f895f 100644 --- a/src/submodule.rs +++ b/src/submodule.rs @@ -20,10 +20,12 @@ pub struct Submodule<'repo> { impl<'repo> Submodule<'repo> { /// Get the submodule's branch. /// - /// Returns `None` if the branch is not valid utf-8 or if the branch is not - /// yet available. - pub fn branch(&self) -> Option<&str> { - self.branch_bytes().and_then(|s| str::from_utf8(s).ok()) + /// Returns `Ok(None)` if the branch is not yet available. + pub fn branch(&self) -> Result, Error> { + match self.branch_bytes() { + Some(bb) => str::from_utf8(bb).map(|s| Some(s)).map_err(|e| e.into()), + None => Ok(None), + } } /// Get the branch for the submodule. @@ -54,9 +56,12 @@ impl<'repo> Submodule<'repo> { /// Get the submodule's URL. /// - /// Returns `None` if the URL is not valid utf-8 or if the URL isn't present - pub fn url(&self) -> Option<&str> { - self.opt_url_bytes().and_then(|b| str::from_utf8(b).ok()) + /// Returns `Ok(None)` if the URL isn't present + pub fn url(&self) -> Result, Error> { + match self.opt_url_bytes() { + Some(oub) => str::from_utf8(oub).map(|s| Some(s)).map_err(|e| e.into()), + None => Ok(None), + } } /// Get the URL for the submodule. @@ -76,10 +81,8 @@ impl<'repo> Submodule<'repo> { } /// Get the submodule's name. - /// - /// Returns `None` if the name is not valid utf-8 - pub fn name(&self) -> Option<&str> { - str::from_utf8(self.name_bytes()).ok() + pub fn name(&self) -> Result<&str, Error> { + str::from_utf8(self.name_bytes()).map_err(|e| e.into()) } /// Get the name for the submodule. @@ -353,9 +356,9 @@ mod tests { let mut submodules = repo.submodules().unwrap(); assert_eq!(submodules.len(), 2); let mut s = submodules.remove(0); - assert_eq!(s.name(), Some("bar")); - assert_eq!(s.url(), Some("/path/to/nowhere")); - assert_eq!(s.branch(), None); + assert_eq!(s.name(), Ok("bar")); + assert_eq!(s.url(), Ok(Some("/path/to/nowhere"))); + assert_eq!(s.branch(), Ok(None)); assert!(s.head_id().is_none()); assert!(s.index_id().is_none()); assert!(s.workdir_id().is_none()); @@ -461,7 +464,10 @@ mod tests { // First init child t!(child.init(false)); - assert_eq!(child.url().unwrap(), url_child.as_str()); + assert_eq!( + child.url().expect("Should be okay").unwrap(), + url_child.as_str() + ); // open() is not possible before initializing the repo assert!(child.open().is_err()); diff --git a/src/tag.rs b/src/tag.rs index 6986c7c160..d60fd23e8e 100644 --- a/src/tag.rs +++ b/src/tag.rs @@ -36,9 +36,12 @@ impl<'repo> Tag<'repo> { /// Get the message of a tag /// - /// Returns None if there is no message or if it is not valid utf8 - pub fn message(&self) -> Option<&str> { - self.message_bytes().and_then(|s| str::from_utf8(s).ok()) + /// Returns Ok(None) if there is no message + pub fn message(&self) -> Result, Error> { + match self.message_bytes() { + Some(mb) => str::from_utf8(mb).map(|s| Some(s)).map_err(|e| e.into()), + None => Ok(None), + } } /// Get the message of a tag @@ -49,10 +52,8 @@ impl<'repo> Tag<'repo> { } /// Get the name of a tag - /// - /// Returns None if it is not valid utf8 - pub fn name(&self) -> Option<&str> { - str::from_utf8(self.name_bytes()).ok() + pub fn name(&self) -> Result<&str, Error> { + str::from_utf8(self.name_bytes()).map_err(|e| e.into()) } /// Get the name of a tag @@ -120,7 +121,7 @@ impl<'repo> Tag<'repo> { impl<'repo> std::fmt::Debug for Tag<'repo> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { let mut ds = f.debug_struct("Tag"); - if let Some(name) = self.name() { + if let Ok(name) = self.name() { ds.field("name", &name); } ds.field("id", &self.id()); @@ -194,10 +195,10 @@ mod tests { let tags = repo.tag_names(None).unwrap(); assert_eq!(tags.len(), 1); - assert_eq!(tags.get(0), Some("foo")); + assert_eq!(tags.get(0), Ok(Some("foo"))); - assert_eq!(tag.name(), Some("foo")); - assert_eq!(tag.message(), Some("msg")); + assert_eq!(tag.name(), Ok("foo")); + assert_eq!(tag.message(), Ok(Some("msg"))); assert_eq!(tag.peel().unwrap().id(), obj.id()); assert_eq!(tag.target_id(), obj.id()); assert_eq!(tag.target_type(), Some(crate::ObjectType::Commit)); diff --git a/src/transaction.rs b/src/transaction.rs index 4f661f1d48..e401a16e71 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -178,7 +178,7 @@ mod tests { .unwrap() .symbolic_target() .unwrap(), - "refs/heads/main" + Some("refs/heads/main") ); } diff --git a/src/tree.rs b/src/tree.rs index e683257436..fc70f4670d 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -289,10 +289,8 @@ impl<'tree> TreeEntry<'tree> { } /// Get the filename of a tree entry - /// - /// Returns `None` if the name is not valid utf-8 - pub fn name(&self) -> Option<&str> { - str::from_utf8(self.name_bytes()).ok() + pub fn name(&self) -> Result<&str, Error> { + str::from_utf8(self.name_bytes()).map_err(|e| e.into()) } /// Get the filename of a tree entry @@ -498,7 +496,7 @@ mod tests { assert_eq!(tree.len(), 8); let mut it = tree.iter(); let e = it.nth(4).unwrap(); - assert_eq!(e.name(), Some("f4")); + assert_eq!(e.name(), Ok("f4")); } fn setup_repo(td: &TempDir, repo: &Repository) { @@ -547,7 +545,7 @@ mod tests { assert!(e0 == tree.get_name("f0").unwrap()); assert!(e0 == tree.get_name_bytes(b"f0").unwrap()); assert!(e0 == tree.get_path(Path::new("f0")).unwrap()); - assert_eq!(e0.name(), Some("f0")); + assert_eq!(e0.name(), Ok("f0")); e0.to_object(&repo).unwrap(); let e1 = tree.get(1).unwrap(); @@ -555,7 +553,7 @@ mod tests { assert!(e1 == tree.get_name("f1").unwrap()); assert!(e1 == tree.get_name_bytes(b"f1").unwrap()); assert!(e1 == tree.get_path(Path::new("f1")).unwrap()); - assert_eq!(e1.name(), Some("f1")); + assert_eq!(e1.name(), Ok("f1")); e1.to_object(&repo).unwrap(); } tree.into_object(); @@ -584,7 +582,7 @@ mod tests { let mut ct = 0; tree.walk(TreeWalkMode::PreOrder, |_, entry| { - assert_eq!(entry.name(), Some(format!("f{ct}").as_str())); + assert_eq!(entry.name(), Ok(format!("f{ct}").as_str())); ct += 1; 0 }) @@ -593,7 +591,7 @@ mod tests { let mut ct = 0; tree.walk(TreeWalkMode::PreOrder, |_, entry| { - assert_eq!(entry.name(), Some(format!("f{ct}").as_str())); + assert_eq!(entry.name(), Ok(format!("f{ct}").as_str())); ct += 1; TreeWalkResult::Ok }) diff --git a/src/treebuilder.rs b/src/treebuilder.rs index eba7f45a9c..dce5511af0 100644 --- a/src/treebuilder.rs +++ b/src/treebuilder.rs @@ -196,7 +196,7 @@ mod tests { let tree = builder.write().unwrap(); let tree = repo.find_tree(tree).unwrap(); let entry = tree.get(0).unwrap(); - assert_eq!(entry.name(), Some("name")); + assert_eq!(entry.name(), Ok("name")); let blob = entry.to_object(&repo).unwrap(); let blob = blob.as_blob().unwrap(); assert_eq!(blob.content(), b"data"); diff --git a/src/worktree.rs b/src/worktree.rs index fc32902db1..f0d98c94a9 100644 --- a/src/worktree.rs +++ b/src/worktree.rs @@ -56,10 +56,11 @@ impl Worktree { /// This is the name that can be passed to repo::Repository::find_worktree /// to reopen the worktree. This is also the name that would appear in the /// list returned by repo::Repository::worktrees - pub fn name(&self) -> Option<&str> { - unsafe { - crate::opt_bytes(self, raw::git_worktree_name(self.raw)) - .and_then(|s| str::from_utf8(s).ok()) + pub fn name(&self) -> Result, Error> { + let opt_bytes = unsafe { crate::opt_bytes(self, raw::git_worktree_name(self.raw)) }; + match opt_bytes { + Some(ob) => str::from_utf8(ob).map(|s| Some(s)).map_err(|e| e.into()), + None => Ok(None), } } @@ -278,7 +279,7 @@ mod tests { let opts = WorktreeAddOptions::new(); let wt = repo.worktree("tree-no-ref", &wt_path, Some(&opts)).unwrap(); - assert_eq!(wt.name(), Some("tree-no-ref")); + assert_eq!(wt.name(), Ok(Some("tree-no-ref"))); assert_eq!( wt.path().canonicalize().unwrap(), wt_path.canonicalize().unwrap() @@ -299,7 +300,7 @@ mod tests { let wt = repo.worktree("locked-tree", &wt_path, Some(&opts)).unwrap(); // shouldn't be able to lock a worktree that was created locked assert!(wt.lock(Some("my reason")).is_err()); - assert_eq!(wt.name(), Some("locked-tree")); + assert_eq!(wt.name(), Ok(Some("locked-tree"))); assert_eq!( wt.path().canonicalize().unwrap(), wt_path.canonicalize().unwrap() @@ -326,7 +327,7 @@ mod tests { let wt = repo .worktree("test-worktree", &wt_path, Some(&opts)) .unwrap(); - assert_eq!(wt.name(), Some("test-worktree")); + assert_eq!(wt.name(), Ok(Some("test-worktree"))); assert_eq!( wt.path().canonicalize().unwrap(), wt_path.canonicalize().unwrap() diff --git a/tests/add_extensions.rs b/tests/add_extensions.rs index d49c33cf79..3cafa60eef 100644 --- a/tests/add_extensions.rs +++ b/tests/add_extensions.rs @@ -10,7 +10,8 @@ fn test_add_extensions() -> Result<(), Error> { } let extensions = unsafe { get_extensions() }?; - let extensions: Vec<_> = extensions.iter().collect(); + let extensions: Result, Error> = extensions.iter().collect(); + let extensions = extensions.unwrap(); assert_eq!( extensions, diff --git a/tests/get_extensions.rs b/tests/get_extensions.rs index 2ac362d0ba..db8e456a85 100644 --- a/tests/get_extensions.rs +++ b/tests/get_extensions.rs @@ -6,7 +6,8 @@ use git2::Error; #[test] fn test_get_extensions() -> Result<(), Error> { let extensions = unsafe { get_extensions() }?; - let extensions: Vec<_> = extensions.iter().collect(); + let extensions: Result, Error> = extensions.iter().collect(); + let extensions = extensions.unwrap(); assert_eq!( extensions, diff --git a/tests/remove_extensions.rs b/tests/remove_extensions.rs index 3e54b427b7..5a3e21d170 100644 --- a/tests/remove_extensions.rs +++ b/tests/remove_extensions.rs @@ -18,7 +18,8 @@ fn test_remove_extensions() -> Result<(), Error> { } let extensions = unsafe { get_extensions() }?; - let extensions: Vec<_> = extensions.iter().collect(); + let extensions: Result, Error> = extensions.iter().collect(); + let extensions = extensions.unwrap(); assert_eq!(extensions, [Some("custom"), Some("other")]);