Merge remote-tracking branch 'origin/git2-trailers'

* origin/git2-trailers:
  git-commit: remove dependency on git-trailers

Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com>
This commit is contained in:
Fintan Halpenny
2023-02-16 17:05:49 +00:00
4 changed files with 228 additions and 23 deletions
+1 -5
View File
@@ -8,7 +8,7 @@ authors = [
"Fintan Halpenny <fintan.halpenny@gmail.com>",
]
description = "A small library for parsing, displaying and creating a git commit"
keywords = ["git", "radicle"]
keywords = ["git", "git-commit", "git-trailers", "radicle"]
[dependencies]
thiserror = "1"
@@ -17,7 +17,3 @@ thiserror = "1"
version = "0.16.1"
default-features = false
features = ["vendored-libgit2"]
[dependencies.git-trailers]
version = "0.1.0"
path = "../git-trailers"
+12 -17
View File
@@ -17,7 +17,6 @@ use std::{
};
use git2::{ObjectType, Oid};
use git_trailers::{self as trailers, OwnedTrailer, Trailer};
pub mod author;
pub use author::Author;
@@ -25,6 +24,9 @@ pub use author::Author;
pub mod headers;
pub use headers::{Headers, Signature};
pub mod trailers;
pub use trailers::{OwnedTrailer, Trailer, Trailers};
/// A git commit in its object description form, i.e. the output of
/// `git cat-file` for a commit object.
#[derive(Debug)]
@@ -162,8 +164,6 @@ pub mod error {
InvalidFormat,
#[error("missing '{0}' while parsing commit")]
Missing(&'static str),
#[error(transparent)]
Token(#[from] git_trailers::InvalidToken),
#[error("error occurred while checking for git-trailers: {0}")]
Trailers(#[source] git2::Error),
#[error(transparent)]
@@ -243,19 +243,14 @@ impl FromStr for Commit {
}
}
let (message, trailers) = message.lines().fold(
(Vec::new(), Vec::new()),
|(mut message, mut trailers), line| match trailers::parser::trailer(line, ": ") {
Ok((_, trailer)) => {
trailers.push(trailer.into());
(message, trailers)
},
Err(_) => {
message.push(line);
(message, trailers)
},
},
);
let trailers = trailers::Trailers::parse(message).map_err(error::Parse::Trailers)?;
let message = message
.strip_suffix(&trailers.to_string(": "))
.unwrap_or(message)
.to_string();
let trailers = trailers.iter().map(OwnedTrailer::from).collect();
Ok(Self {
tree,
@@ -263,7 +258,7 @@ impl FromStr for Commit {
author: author.ok_or(error::Parse::Missing("author"))?,
committer: committer.ok_or(error::Parse::Missing("committer"))?,
headers,
message: message.join("\n"),
message,
trailers,
})
}
+214
View File
@@ -0,0 +1,214 @@
use std::{borrow::Cow, fmt, fmt::Write, ops::Deref, str::FromStr};
use git2::{MessageTrailersStrs, MessageTrailersStrsIterator};
/// A Git commit's set of trailers that are left in the commit's
/// message.
///
/// Trailers are key/value pairs in the last paragraph of a message,
/// not including any patches or conflicts that may be present.
///
/// # Usage
///
/// To construct `Trailers`, you can use [`Trailers::parse`] or its
/// `FromStr` implementation.
///
/// To iterate over the trailers, you can use [`Trailers::iter`].
///
/// To render the trailers to a `String`, you can use
/// [`Trailers::to_string`] or its `Display` implementation (note that
/// it will default to using `": "` as the separator.
///
/// # Examples
///
/// ```text
/// Add new functionality
///
/// Making code better with new functionality.
///
/// X-Signed-Off-By: Alex Sellier
/// X-Co-Authored-By: Fintan Halpenny
/// ```
///
/// The trailers in the above example are:
///
/// ```text
/// X-Signed-Off-By: Alex Sellier
/// X-Co-Authored-By: Fintan Halpenny
/// ```
pub struct Trailers {
inner: MessageTrailersStrs,
}
impl Trailers {
pub fn parse(message: &str) -> Result<Self, git2::Error> {
Ok(Self {
inner: git2::message_trailers_strs(message)?,
})
}
pub fn iter(&self) -> Iter<'_> {
Iter {
inner: self.inner.iter(),
}
}
pub fn to_string<'a, S>(&self, sep: S) -> String
where
S: Separator<'a>,
{
let mut buf = String::new();
for (i, trailer) in self.iter().enumerate() {
if i > 0 {
writeln!(buf).ok();
}
write!(buf, "{}", trailer.display(sep.sep_for(&trailer.token))).ok();
}
writeln!(buf).ok();
buf
}
}
impl fmt::Display for Trailers {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.to_string(": "))
}
}
pub trait Separator<'a> {
fn sep_for(&self, token: &Token) -> &'a str;
}
impl<'a> Separator<'a> for &'a str {
fn sep_for(&self, _: &Token) -> &'a str {
self
}
}
impl<'a, F> Separator<'a> for F
where
F: Fn(&Token) -> &'a str,
{
fn sep_for(&self, token: &Token) -> &'a str {
self(token)
}
}
impl FromStr for Trailers {
type Err = git2::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse(s)
}
}
pub struct Iter<'a> {
inner: MessageTrailersStrsIterator<'a>,
}
impl<'a> Iterator for Iter<'a> {
type Item = Trailer<'a>;
fn next(&mut self) -> Option<Self::Item> {
let (token, value) = self.inner.next()?;
Some(Trailer {
token: Token(token),
value: Cow::Borrowed(value),
})
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Token<'a>(&'a str);
impl Deref for Token<'_> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.0
}
}
pub struct Display<'a> {
trailer: &'a Trailer<'a>,
separator: &'a str,
}
impl<'a> fmt::Display for Display<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}{}{}",
self.trailer.token.deref(),
self.separator,
self.trailer.value,
)
}
}
/// A trailer is a key/value pair found in the last paragraph of a Git
/// commit message, not including any patches or conflicts that may be
/// present.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Trailer<'a> {
pub token: Token<'a>,
pub value: Cow<'a, str>,
}
impl<'a> Trailer<'a> {
pub fn display(&'a self, separator: &'a str) -> Display<'a> {
Display {
trailer: self,
separator,
}
}
pub fn to_owned(&self) -> OwnedTrailer {
OwnedTrailer::from(self)
}
}
/// A version of the [`Trailer`] which owns its token and
/// value. Useful for when you need to carry trailers around in a long
/// lived data structure.
#[derive(Debug)]
pub struct OwnedTrailer {
pub token: OwnedToken,
pub value: String,
}
#[derive(Debug)]
pub struct OwnedToken(String);
impl Deref for OwnedToken {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'a> From<&Trailer<'a>> for OwnedTrailer {
fn from(t: &Trailer<'a>) -> Self {
OwnedTrailer {
token: OwnedToken(t.token.0.to_string()),
value: t.value.to_string(),
}
}
}
impl<'a> From<Trailer<'a>> for OwnedTrailer {
fn from(t: Trailer<'a>) -> Self {
(&t).into()
}
}
impl<'a> From<&'a OwnedTrailer> for Trailer<'a> {
fn from(t: &'a OwnedTrailer) -> Self {
Trailer {
token: Token(t.token.0.as_str()),
value: Cow::from(&t.value),
}
}
}
+1 -1
View File
@@ -14,7 +14,7 @@ doc = false
[features]
test = []
[dependencies.git-commit]
[dev-dependencies.git-commit]
path = ".."
[dev-dependencies.git2]