176 lines
4.9 KiB
Rust
176 lines
4.9 KiB
Rust
use {
|
|
crate::{
|
|
error::Error, extended::Extended, rapid::Rapid, semver::SemVer, simple::Simple,
|
|
version::Kind, version::Version,
|
|
},
|
|
chrono::{offset::Utc, DateTime},
|
|
serde::{Deserialize, Serialize},
|
|
std::{cmp::Ordering, fmt, str::FromStr},
|
|
};
|
|
|
|
/// Represents a Git revisionrather than a regular release.
|
|
/// A `GitRev` release contains the revision's hash and the Date/Time of the release.
|
|
/// Git revisions are not the preferred method of distribution for obvious reasons, as
|
|
/// they generally do not represent a stable release of the code. Another drawback is
|
|
/// that a Git revision can only be compared against another Git revision, making it
|
|
/// impossible to properly order updates between a proper `SemVer` type release and a\
|
|
/// Git revision.
|
|
/// ### Notes on display formatting
|
|
/// The hash as stored in this struct should be the short form revision hash (the first
|
|
/// 7 characters of the full hash). The full date and time information is saved minus
|
|
/// any fractional seconds. When displaying, the date and time should be converted to a
|
|
/// Unix timestamp.
|
|
/// ### Parsing from a string
|
|
/// In order to parse this struct from a string, two fields `hash` and `datetime` should
|
|
/// be separated by a period character '.' and the date/time take the form of a Unix
|
|
/// timestamp. This information can be retrieved from git for a given commit using the
|
|
/// following git commandline:
|
|
/// ```Sh
|
|
/// git show --pretty=format:"%h.%at" <hash> | head -n 1
|
|
/// ```
|
|
/// The resulting string can then be parsed using the `FromStr` trait, which provides
|
|
/// both `from_str` and `parse`.
|
|
/// ### Comparison - ordering
|
|
/// Git revisions are ordered by date/time
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
pub struct GitRev {
|
|
/// the short revision hash
|
|
pub hash: String,
|
|
/// the time of the revision commit
|
|
pub datetime: DateTime<Utc>,
|
|
}
|
|
|
|
impl fmt::Display for GitRev {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "git_{}.{}", self.hash, self.datetime.timestamp())
|
|
}
|
|
}
|
|
|
|
impl PartialOrd for GitRev {
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
Some(self.cmp(other))
|
|
}
|
|
}
|
|
|
|
impl Ord for GitRev {
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
self.datetime.cmp(&other.datetime)
|
|
}
|
|
}
|
|
|
|
impl FromStr for GitRev {
|
|
type Err = Error;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
if let Some(gitrev) = s.strip_prefix("git_") {
|
|
if let Some((hash, datetime)) = gitrev.split_once('.') {
|
|
if hash.len() == 7 {
|
|
let secs: i64 = datetime.parse()?;
|
|
let Some(datetime) = DateTime::<Utc>::from_timestamp(secs, 0) else {
|
|
return Err(Error::ParseInt);
|
|
};
|
|
return Ok(Self {
|
|
hash: hash.to_string(),
|
|
datetime,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
Err(Error::ParseGitRev)
|
|
}
|
|
}
|
|
|
|
impl PartialEq<Extended> for GitRev {
|
|
fn eq(&self, _other: &Extended) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
impl PartialOrd<Extended> for GitRev {
|
|
fn partial_cmp(&self, _other: &Extended) -> Option<Ordering> {
|
|
None
|
|
}
|
|
}
|
|
|
|
impl PartialEq<SemVer> for GitRev {
|
|
fn eq(&self, _other: &SemVer) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
impl PartialOrd<SemVer> for GitRev {
|
|
fn partial_cmp(&self, _other: &SemVer) -> Option<Ordering> {
|
|
None
|
|
}
|
|
}
|
|
|
|
impl PartialEq<Rapid> for GitRev {
|
|
fn eq(&self, _other: &Rapid) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
impl PartialOrd<Rapid> for GitRev {
|
|
fn partial_cmp(&self, _other: &Rapid) -> Option<Ordering> {
|
|
None
|
|
}
|
|
}
|
|
|
|
impl PartialEq<Simple> for GitRev {
|
|
fn eq(&self, _other: &Simple) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
impl PartialOrd<Simple> for GitRev {
|
|
fn partial_cmp(&self, _other: &Simple) -> Option<Ordering> {
|
|
None
|
|
}
|
|
}
|
|
|
|
impl TryFrom<Version> for GitRev {
|
|
type Error = Error;
|
|
|
|
fn try_from(value: Version) -> Result<Self, Self::Error> {
|
|
match value.0 {
|
|
Kind::GitRev(g) => Ok(g),
|
|
_ => Err(Error::ParseGitRev),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use std::{thread, time::Duration};
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn ord() {
|
|
let a = GitRev {
|
|
hash: "aaabxxx".to_string(),
|
|
datetime: Utc::now(),
|
|
};
|
|
thread::sleep(Duration::from_millis(10));
|
|
let b = GitRev {
|
|
hash: "aaaaxxx".to_string(),
|
|
datetime: Utc::now(),
|
|
};
|
|
assert!(a < b);
|
|
}
|
|
|
|
#[test]
|
|
fn from_str() {
|
|
let now = DateTime::<Utc>::from_timestamp(Utc::now().timestamp(), 0).unwrap();
|
|
let rev: GitRev = format!("git_r2d2xxx.{}", now.timestamp()).parse().unwrap();
|
|
println!("Version = {rev}");
|
|
assert_eq!(
|
|
rev,
|
|
GitRev {
|
|
hash: "r2d2xxx".to_string(),
|
|
datetime: now,
|
|
}
|
|
);
|
|
}
|
|
}
|