use crate::digest::Digest;
use strum::{AsRefStr, EnumDiscriminants, EnumIter, EnumString};
#[derive(Clone, Copy, Debug, PartialEq, Eq, AsRefStr, EnumString, EnumIter)]
pub enum BlobMode {
#[strum(serialize = "100644")]
Regular,
#[strum(serialize = "100755")]
Executable,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumDiscriminants)]
#[strum_discriminants(name(BlobContentTag))]
#[strum_discriminants(derive(AsRefStr, EnumString, EnumIter))]
#[strum_discriminants(strum(serialize_all = "kebab-case"))]
pub enum BlobContent {
Available {
raw_digest: Digest,
projection_digest: Digest,
},
LfsPointer {
raw_digest: Digest,
},
}
impl BlobContent {
#[must_use]
pub const fn projection_digest(self) -> Option<Digest> {
match self {
Self::Available {
projection_digest, ..
} => Some(projection_digest),
Self::LfsPointer { .. } => None,
}
}
#[must_use]
pub const fn is_lfs_pointer(self) -> bool {
matches!(self, Self::LfsPointer { .. })
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BlobTarget<P> {
pub path: P,
pub mode: BlobMode,
pub content: BlobContent,
}
#[derive(Clone, Debug, PartialEq, Eq, EnumDiscriminants)]
#[strum_discriminants(name(TargetTag))]
#[strum_discriminants(derive(AsRefStr, EnumString, EnumIter))]
#[strum_discriminants(strum(serialize_all = "kebab-case"))]
pub enum Target<P> {
Tree { path: P },
Blob(BlobTarget<P>),
}
impl<P> Target<P> {
#[must_use]
pub const fn projection_digest(&self) -> Option<Digest> {
match self {
Self::Tree { .. } => None,
Self::Blob(blob) => blob.content.projection_digest(),
}
}
#[must_use]
pub const fn is_lfs_pointer(&self) -> bool {
match self {
Self::Tree { .. } => false,
Self::Blob(blob) => blob.content.is_lfs_pointer(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, EnumDiscriminants)]
#[strum_discriminants(name(MissingTag))]
#[strum_discriminants(derive(AsRefStr, EnumString, EnumIter))]
#[strum_discriminants(strum(serialize_all = "kebab-case"))]
pub enum Missing<P> {
PathNotFound { path: P },
LineFragmentOutOfRange { path: P },
}
#[derive(Clone, Debug, PartialEq, Eq, EnumDiscriminants)]
#[strum_discriminants(name(UnsupportedTargetTag))]
#[strum_discriminants(derive(AsRefStr, EnumString, EnumIter))]
#[strum_discriminants(strum(serialize_all = "kebab-case"))]
pub enum UnsupportedTarget<P> {
Symlink { path: P },
Gitlink { path: P },
}
#[derive(Clone, Debug, PartialEq, Eq, EnumDiscriminants)]
#[strum_discriminants(name(UnsupportedSemanticsTag))]
#[strum_discriminants(derive(AsRefStr, EnumString, EnumIter))]
#[strum_discriminants(strum(serialize_all = "kebab-case"))]
pub enum UnsupportedSemantics<P> {
Query(Target<P>),
Fragment(BlobTarget<P>),
CodeFragment(Target<P>),
SiteRoute,
NetworkPath,
}
impl<P> UnsupportedSemantics<P> {
#[must_use]
pub const fn is_lfs_pointer(&self) -> bool {
match self {
Self::Query(target) | Self::CodeFragment(target) => target.is_lfs_pointer(),
Self::Fragment(blob) => blob.content.is_lfs_pointer(),
Self::SiteRoute | Self::NetworkPath => false,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, EnumDiscriminants)]
#[strum_discriminants(name(VersionScopeTag))]
#[strum_discriminants(derive(AsRefStr, EnumString, EnumIter))]
#[strum_discriminants(strum(serialize_all = "kebab-case"))]
pub enum VersionScope<P> {
KnownPath { path: P },
UnknownPath,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, AsRefStr, EnumString, EnumIter)]
#[strum(serialize_all = "kebab-case")]
pub enum InvalidReference {
Uri,
PercentEncoding,
DecodedPathControl,
PathTraversal,
BackslashSeparator,
EncodedSlash,
FragmentEncoding,
Syntax,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, AsRefStr, EnumString, EnumIter)]
#[strum(serialize_all = "kebab-case")]
pub enum ExternalReference {
Url,
ForeignRepository,
}
#[derive(Clone, Debug, PartialEq, Eq, EnumDiscriminants)]
#[strum_discriminants(name(ResolutionTag))]
#[strum_discriminants(derive(AsRefStr, EnumString, EnumIter))]
#[strum_discriminants(strum(serialize_all = "kebab-case"))]
pub enum Resolution<P> {
Resolved(Target<P>),
Missing(Missing<P>),
TypeMismatch(Target<P>),
UnsupportedTarget(UnsupportedTarget<P>),
UnsupportedSemantics(UnsupportedSemantics<P>),
UnsupportedVersion(VersionScope<P>),
Invalid(InvalidReference),
External(ExternalReference),
}
impl<P> Resolution<P> {
#[must_use]
pub const fn is_lfs_pointer(&self) -> bool {
match self {
Self::Resolved(target) | Self::TypeMismatch(target) => target.is_lfs_pointer(),
Self::UnsupportedSemantics(semantics) => semantics.is_lfs_pointer(),
Self::Missing(_)
| Self::UnsupportedTarget(_)
| Self::UnsupportedVersion(_)
| Self::Invalid(_)
| Self::External(_) => false,
}
}
}