1use std::collections::BTreeSet;
2
3use gix_hash::ObjectId;
4
5use crate::{
6 packed, peel,
7 raw::Reference,
8 store_impl::{file, file::log},
9 Target,
10};
11
12pub trait Sealed {}
13impl Sealed for crate::Reference {}
14
15pub trait ReferenceExt: Sealed {
17 fn log_iter<'a, 's>(&'a self, store: &'s file::Store) -> log::iter::Platform<'a, 's>;
19
20 fn log_exists(&self, store: &file::Store) -> bool;
22
23 #[deprecated = "Use `peel_to_id()` instead"]
30 fn peel_to_id_in_place(
31 &mut self,
32 store: &file::Store,
33 objects: &dyn gix_object::Find,
34 ) -> Result<ObjectId, peel::to_id::Error>;
35
36 fn peel_to_id(
46 &mut self,
47 store: &file::Store,
48 objects: &dyn gix_object::Find,
49 ) -> Result<ObjectId, peel::to_id::Error>;
50
51 #[deprecated = "Use `peel_to_id_packed()` instead"]
54 fn peel_to_id_in_place_packed(
55 &mut self,
56 store: &file::Store,
57 objects: &dyn gix_object::Find,
58 packed: Option<&packed::Buffer>,
59 ) -> Result<ObjectId, peel::to_id::Error>;
60
61 fn peel_to_id_packed(
64 &mut self,
65 store: &file::Store,
66 objects: &dyn gix_object::Find,
67 packed: Option<&packed::Buffer>,
68 ) -> Result<ObjectId, peel::to_id::Error>;
69
70 #[deprecated = "Use `follow_to_object_packed()` instead"]
73 fn follow_to_object_in_place_packed(
74 &mut self,
75 store: &file::Store,
76 packed: Option<&packed::Buffer>,
77 ) -> Result<ObjectId, peel::to_object::Error>;
78
79 fn follow_to_object_packed(
82 &mut self,
83 store: &file::Store,
84 packed: Option<&packed::Buffer>,
85 ) -> Result<ObjectId, peel::to_object::Error>;
86
87 fn follow(&self, store: &file::Store) -> Option<Result<Reference, file::find::existing::Error>>;
91
92 fn follow_packed(
97 &self,
98 store: &file::Store,
99 packed: Option<&packed::Buffer>,
100 ) -> Option<Result<Reference, file::find::existing::Error>>;
101}
102
103impl ReferenceExt for Reference {
104 fn log_iter<'a, 's>(&'a self, store: &'s file::Store) -> log::iter::Platform<'a, 's> {
105 log::iter::Platform {
106 store,
107 name: self.name.as_ref(),
108 buf: Vec::new(),
109 }
110 }
111
112 fn log_exists(&self, store: &file::Store) -> bool {
113 store
114 .reflog_exists(self.name.as_ref())
115 .expect("infallible name conversion")
116 }
117
118 fn peel_to_id_in_place(
119 &mut self,
120 store: &file::Store,
121 objects: &dyn gix_object::Find,
122 ) -> Result<ObjectId, peel::to_id::Error> {
123 self.peel_to_id(store, objects)
124 }
125
126 fn peel_to_id(
127 &mut self,
128 store: &file::Store,
129 objects: &dyn gix_object::Find,
130 ) -> Result<ObjectId, peel::to_id::Error> {
131 let packed = store.assure_packed_refs_uptodate().map_err(|err| {
132 peel::to_id::Error::FollowToObject(peel::to_object::Error::Follow(file::find::existing::Error::Find(
133 file::find::Error::PackedOpen(err),
134 )))
135 })?;
136 self.peel_to_id_packed(store, objects, packed.as_ref().map(|b| &***b))
137 }
138
139 fn peel_to_id_in_place_packed(
140 &mut self,
141 store: &file::Store,
142 objects: &dyn gix_object::Find,
143 packed: Option<&packed::Buffer>,
144 ) -> Result<ObjectId, peel::to_id::Error> {
145 self.peel_to_id_packed(store, objects, packed)
146 }
147
148 fn peel_to_id_packed(
149 &mut self,
150 store: &file::Store,
151 objects: &dyn gix_object::Find,
152 packed: Option<&packed::Buffer>,
153 ) -> Result<ObjectId, peel::to_id::Error> {
154 match self.peeled {
155 Some(peeled) => {
156 self.target = Target::Object(peeled.to_owned());
157 Ok(peeled)
158 }
159 None => {
160 let mut oid = self.follow_to_object_packed(store, packed)?;
161 let mut buf = Vec::new();
162 let peeled_id = loop {
163 let gix_object::Data {
164 kind,
165 data,
166 hash_kind: _,
167 } = objects
168 .try_find(&oid, &mut buf)?
169 .ok_or_else(|| peel::to_id::Error::NotFound {
170 oid,
171 name: self.name.0.clone(),
172 })?;
173 match kind {
174 gix_object::Kind::Tag => {
175 oid = gix_object::TagRefIter::from_bytes(data).target_id().map_err(|_err| {
176 peel::to_id::Error::NotFound {
177 oid,
178 name: self.name.0.clone(),
179 }
180 })?;
181 }
182 _ => break oid,
183 }
184 };
185 self.peeled = Some(peeled_id);
186 self.target = Target::Object(peeled_id);
187 Ok(peeled_id)
188 }
189 }
190 }
191
192 fn follow_to_object_in_place_packed(
193 &mut self,
194 store: &file::Store,
195 packed: Option<&packed::Buffer>,
196 ) -> Result<ObjectId, peel::to_object::Error> {
197 self.follow_to_object_packed(store, packed)
198 }
199
200 fn follow_to_object_packed(
201 &mut self,
202 store: &file::Store,
203 packed: Option<&packed::Buffer>,
204 ) -> Result<ObjectId, peel::to_object::Error> {
205 match self.target {
206 Target::Object(id) => Ok(id),
207 Target::Symbolic(_) => {
208 let mut seen = BTreeSet::new();
209 let cursor = &mut *self;
210 while let Some(next) = cursor.follow_packed(store, packed) {
211 let next = next?;
212 if seen.contains(&next.name) {
213 return Err(peel::to_object::Error::Cycle {
214 start_absolute: store.reference_path(cursor.name.as_ref()),
215 });
216 }
217 *cursor = next;
218 seen.insert(cursor.name.clone());
219 const MAX_REF_DEPTH: usize = 5;
220 if seen.len() == MAX_REF_DEPTH {
221 return Err(peel::to_object::Error::DepthLimitExceeded {
222 max_depth: MAX_REF_DEPTH,
223 });
224 }
225 }
226 let oid = self.target.try_id().expect("peeled ref").to_owned();
227 Ok(oid)
228 }
229 }
230 }
231
232 fn follow(&self, store: &file::Store) -> Option<Result<Reference, file::find::existing::Error>> {
233 let packed = match store
234 .assure_packed_refs_uptodate()
235 .map_err(|err| file::find::existing::Error::Find(file::find::Error::PackedOpen(err)))
236 {
237 Ok(packed) => packed,
238 Err(err) => return Some(Err(err)),
239 };
240 self.follow_packed(store, packed.as_ref().map(|b| &***b))
241 }
242
243 fn follow_packed(
244 &self,
245 store: &file::Store,
246 packed: Option<&packed::Buffer>,
247 ) -> Option<Result<Reference, file::find::existing::Error>> {
248 match &self.target {
249 Target::Object(_) => None,
250 Target::Symbolic(full_name) => match store.try_find_packed(full_name.as_ref(), packed) {
251 Ok(Some(next)) => Some(Ok(next)),
252 Ok(None) => Some(Err(file::find::existing::Error::NotFound {
253 name: full_name.to_path().to_owned(),
254 })),
255 Err(err) => Some(Err(file::find::existing::Error::Find(err))),
256 },
257 }
258 }
259}