use std::convert::{TryFrom, TryInto};
use std::ffi::{OsStr, OsString};
use std::fmt::Display;
use std::str::FromStr;
use eyre::Context;
use tracing::instrument;
use crate::git::repo::wrap_git_error;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NonZeroOid {
pub(super) inner: git2::Oid,
}
impl NonZeroOid {
pub fn as_bytes(&self) -> &[u8] {
self.inner.as_bytes()
}
}
impl std::fmt::Debug for NonZeroOid {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "NonZeroOid({:?})", self.inner)
}
}
impl Display for NonZeroOid {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.inner)
}
}
impl TryFrom<MaybeZeroOid> for NonZeroOid {
type Error = eyre::Error;
#[instrument]
fn try_from(value: MaybeZeroOid) -> Result<Self, Self::Error> {
match value {
MaybeZeroOid::NonZero(non_zero_oid) => Ok(non_zero_oid),
MaybeZeroOid::Zero => eyre::bail!("Expected a non-zero OID"),
}
}
}
impl TryFrom<OsString> for NonZeroOid {
type Error = eyre::Error;
#[instrument]
fn try_from(value: OsString) -> Result<Self, Self::Error> {
let value: &OsStr = &value;
value.try_into()
}
}
impl TryFrom<&OsStr> for NonZeroOid {
type Error = eyre::Error;
#[instrument]
fn try_from(value: &OsStr) -> Result<Self, Self::Error> {
let oid: MaybeZeroOid = value.try_into()?;
match oid {
MaybeZeroOid::Zero => eyre::bail!("OID was zero, but expected to be non-zero"),
MaybeZeroOid::NonZero(oid) => Ok(oid),
}
}
}
impl TryFrom<&[u8]> for NonZeroOid {
type Error = eyre::Error;
#[instrument]
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
let oid = MaybeZeroOid::try_from(value)?;
NonZeroOid::try_from(oid)
}
}
impl FromStr for NonZeroOid {
type Err = eyre::Error;
#[instrument]
fn from_str(value: &str) -> Result<Self, Self::Err> {
let oid: MaybeZeroOid = value.parse()?;
match oid {
MaybeZeroOid::NonZero(non_zero_oid) => Ok(non_zero_oid),
MaybeZeroOid::Zero => eyre::bail!("Expected a non-zero OID, but got: {:?}", value),
}
}
}
impl From<NonZeroOid> for git2::Oid {
fn from(oid: NonZeroOid) -> Self {
oid.inner
}
}
pub(super) fn make_non_zero_oid(oid: git2::Oid) -> NonZeroOid {
assert_ne!(oid, git2::Oid::zero());
NonZeroOid { inner: oid }
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MaybeZeroOid {
Zero,
NonZero(NonZeroOid),
}
impl Default for MaybeZeroOid {
fn default() -> Self {
Self::Zero
}
}
impl MaybeZeroOid {
pub fn from_bytes(bytes: &[u8]) -> eyre::Result<Self> {
let oid = git2::Oid::from_bytes(bytes)?;
Ok(oid.into())
}
}
impl std::fmt::Debug for MaybeZeroOid {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self}")
}
}
impl Display for MaybeZeroOid {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let zero = git2::Oid::zero();
write!(
f,
"{:?}",
match self {
MaybeZeroOid::NonZero(NonZeroOid { inner }) => inner,
MaybeZeroOid::Zero => &zero,
}
)
}
}
impl FromStr for MaybeZeroOid {
type Err = eyre::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.parse() {
Ok(oid) if oid == git2::Oid::zero() => Ok(MaybeZeroOid::Zero),
Ok(oid) => Ok(MaybeZeroOid::NonZero(NonZeroOid { inner: oid })),
Err(err) => Err(wrap_git_error(err)).wrap_err("Could not parse OID from string"),
}
}
}
impl From<git2::Oid> for MaybeZeroOid {
fn from(oid: git2::Oid) -> Self {
if oid.is_zero() {
Self::Zero
} else {
Self::NonZero(make_non_zero_oid(oid))
}
}
}
impl TryFrom<&OsStr> for MaybeZeroOid {
type Error = eyre::Error;
fn try_from(value: &OsStr) -> Result<Self, Self::Error> {
match value.to_str() {
None => eyre::bail!("OID value was not a simple ASCII value: {:?}", value),
Some(value) => value.parse(),
}
}
}
impl TryFrom<OsString> for MaybeZeroOid {
type Error = eyre::Error;
fn try_from(value: OsString) -> Result<Self, Self::Error> {
MaybeZeroOid::try_from(value.as_os_str())
}
}
impl TryFrom<&[u8]> for MaybeZeroOid {
type Error = git2::Error;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
let oid = git2::Oid::from_bytes(value)?;
Ok(MaybeZeroOid::from(oid))
}
}
impl From<NonZeroOid> for MaybeZeroOid {
fn from(oid: NonZeroOid) -> Self {
Self::NonZero(oid)
}
}
impl From<Option<NonZeroOid>> for MaybeZeroOid {
fn from(oid: Option<NonZeroOid>) -> Self {
match oid {
Some(oid) => MaybeZeroOid::NonZero(oid),
None => MaybeZeroOid::Zero,
}
}
}
impl From<MaybeZeroOid> for Option<NonZeroOid> {
fn from(oid: MaybeZeroOid) -> Self {
match oid {
MaybeZeroOid::Zero => None,
MaybeZeroOid::NonZero(oid) => Some(oid),
}
}
}
impl From<MaybeZeroOid> for git2::Oid {
fn from(oid: MaybeZeroOid) -> Self {
match oid {
MaybeZeroOid::Zero => git2::Oid::zero(),
MaybeZeroOid::NonZero(oid) => oid.into(),
}
}
}