use std::{cmp::Ordering, error::Error, fmt};
use crate::{Categorized, Category};
#[cfg(test)]
mod tests;
const LARGEST_REVISION: u64 = (1 << 63) - 1;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NotARevision {
revision: String,
}
impl NotARevision {
#[must_use]
pub fn revision(&self) -> &str {
&self.revision
}
}
impl fmt::Display for NotARevision {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "not a revision: {:?}", self.revision)
}
}
impl Error for NotARevision {}
impl Categorized for NotARevision {
fn category(&self) -> Option<Category> {
None
}
}
pub fn parse_revision(revision: &str) -> Result<u64, NotARevision> {
let refused = || NotARevision {
revision: revision.to_owned(),
};
if revision.is_empty() || !revision.bytes().all(|byte| byte.is_ascii_digit()) {
return Err(refused());
}
match revision.parse::<u64>() {
Ok(number) if number <= LARGEST_REVISION => Ok(number),
_ => Err(refused()),
}
}
pub fn compare_revisions(left: &str, right: &str) -> Result<Ordering, NotARevision> {
let left = revision_number(left)?;
let right = revision_number(right)?;
Ok(left.cmp(&right))
}
fn revision_number(revision: &str) -> Result<Option<u64>, NotARevision> {
if revision.is_empty() {
return Ok(None);
}
parse_revision(revision).map(Some)
}