1use crate::digest::Digest;
2use strum::{AsRefStr, EnumDiscriminants, EnumIter, EnumString};
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq, AsRefStr, EnumString, EnumIter)]
7pub enum BlobMode {
8 #[strum(serialize = "100644")]
9 Regular,
10 #[strum(serialize = "100755")]
11 Executable,
12}
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumDiscriminants)]
17#[strum_discriminants(name(BlobContentTag))]
18#[strum_discriminants(derive(AsRefStr, EnumString, EnumIter))]
19#[strum_discriminants(strum(serialize_all = "kebab-case"))]
20pub enum BlobContent {
21 Available {
22 raw_digest: Digest,
23 projection_digest: Digest,
24 },
25 LfsPointer {
26 raw_digest: Digest,
27 },
28}
29
30impl BlobContent {
31 #[must_use]
32 pub const fn projection_digest(self) -> Option<Digest> {
33 match self {
34 Self::Available {
35 projection_digest, ..
36 } => Some(projection_digest),
37 Self::LfsPointer { .. } => None,
38 }
39 }
40
41 #[must_use]
42 pub const fn is_lfs_pointer(self) -> bool {
43 matches!(self, Self::LfsPointer { .. })
44 }
45}
46
47#[derive(Clone, Debug, PartialEq, Eq)]
49pub struct BlobTarget<P> {
50 pub path: P,
51 pub mode: BlobMode,
52 pub content: BlobContent,
53}
54
55#[derive(Clone, Debug, PartialEq, Eq, EnumDiscriminants)]
58#[strum_discriminants(name(TargetTag))]
59#[strum_discriminants(derive(AsRefStr, EnumString, EnumIter))]
60#[strum_discriminants(strum(serialize_all = "kebab-case"))]
61pub enum Target<P> {
62 Tree { path: P },
63 Blob(BlobTarget<P>),
64}
65
66impl<P> Target<P> {
67 #[must_use]
68 pub const fn projection_digest(&self) -> Option<Digest> {
69 match self {
70 Self::Tree { .. } => None,
71 Self::Blob(blob) => blob.content.projection_digest(),
72 }
73 }
74
75 #[must_use]
76 pub const fn is_lfs_pointer(&self) -> bool {
77 match self {
78 Self::Tree { .. } => false,
79 Self::Blob(blob) => blob.content.is_lfs_pointer(),
80 }
81 }
82}
83
84#[derive(Clone, Debug, PartialEq, Eq, EnumDiscriminants)]
87#[strum_discriminants(name(MissingTag))]
88#[strum_discriminants(derive(AsRefStr, EnumString, EnumIter))]
89#[strum_discriminants(strum(serialize_all = "kebab-case"))]
90pub enum Missing<P> {
91 PathNotFound { path: P },
92 LineFragmentOutOfRange { path: P },
93}
94
95#[derive(Clone, Debug, PartialEq, Eq, EnumDiscriminants)]
98#[strum_discriminants(name(UnsupportedTargetTag))]
99#[strum_discriminants(derive(AsRefStr, EnumString, EnumIter))]
100#[strum_discriminants(strum(serialize_all = "kebab-case"))]
101pub enum UnsupportedTarget<P> {
102 Symlink { path: P },
103 Gitlink { path: P },
104}
105
106#[derive(Clone, Debug, PartialEq, Eq, EnumDiscriminants)]
109#[strum_discriminants(name(UnsupportedSemanticsTag))]
110#[strum_discriminants(derive(AsRefStr, EnumString, EnumIter))]
111#[strum_discriminants(strum(serialize_all = "kebab-case"))]
112pub enum UnsupportedSemantics<P> {
113 Query(Target<P>),
114 Fragment(BlobTarget<P>),
115 CodeFragment(Target<P>),
116 SiteRoute,
117 NetworkPath,
118}
119
120impl<P> UnsupportedSemantics<P> {
121 #[must_use]
122 pub const fn is_lfs_pointer(&self) -> bool {
123 match self {
124 Self::Query(target) | Self::CodeFragment(target) => target.is_lfs_pointer(),
125 Self::Fragment(blob) => blob.content.is_lfs_pointer(),
126 Self::SiteRoute | Self::NetworkPath => false,
127 }
128 }
129}
130
131#[derive(Clone, Debug, PartialEq, Eq, EnumDiscriminants)]
134#[strum_discriminants(name(VersionScopeTag))]
135#[strum_discriminants(derive(AsRefStr, EnumString, EnumIter))]
136#[strum_discriminants(strum(serialize_all = "kebab-case"))]
137pub enum VersionScope<P> {
138 KnownPath { path: P },
139 UnknownPath,
140}
141
142#[derive(Clone, Copy, Debug, PartialEq, Eq, AsRefStr, EnumString, EnumIter)]
145#[strum(serialize_all = "kebab-case")]
146pub enum InvalidReference {
147 Uri,
148 PercentEncoding,
149 DecodedPathControl,
150 PathTraversal,
151 BackslashSeparator,
152 EncodedSlash,
153 FragmentEncoding,
154 Syntax,
155}
156
157#[derive(Clone, Copy, Debug, PartialEq, Eq, AsRefStr, EnumString, EnumIter)]
160#[strum(serialize_all = "kebab-case")]
161pub enum ExternalReference {
162 Url,
163 ForeignRepository,
164}
165
166#[derive(Clone, Debug, PartialEq, Eq, EnumDiscriminants)]
170#[strum_discriminants(name(ResolutionTag))]
171#[strum_discriminants(derive(AsRefStr, EnumString, EnumIter))]
172#[strum_discriminants(strum(serialize_all = "kebab-case"))]
173pub enum Resolution<P> {
174 Resolved(Target<P>),
175 Missing(Missing<P>),
176 TypeMismatch(Target<P>),
177 UnsupportedTarget(UnsupportedTarget<P>),
178 UnsupportedSemantics(UnsupportedSemantics<P>),
179 UnsupportedVersion(VersionScope<P>),
180 Invalid(InvalidReference),
181 External(ExternalReference),
182}
183
184impl<P> Resolution<P> {
185 #[must_use]
186 pub const fn is_lfs_pointer(&self) -> bool {
187 match self {
188 Self::Resolved(target) | Self::TypeMismatch(target) => target.is_lfs_pointer(),
189 Self::UnsupportedSemantics(semantics) => semantics.is_lfs_pointer(),
190 Self::Missing(_)
191 | Self::UnsupportedTarget(_)
192 | Self::UnsupportedVersion(_)
193 | Self::Invalid(_)
194 | Self::External(_) => false,
195 }
196 }
197}