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 { kind, data } =
164 objects
165 .try_find(&oid, &mut buf)?
166 .ok_or_else(|| peel::to_id::Error::NotFound {
167 oid,
168 name: self.name.0.clone(),
169 })?;
170 match kind {
171 gix_object::Kind::Tag => {
172 oid = gix_object::TagRefIter::from_bytes(data).target_id().map_err(|_err| {
173 peel::to_id::Error::NotFound {
174 oid,
175 name: self.name.0.clone(),
176 }
177 })?;
178 }
179 _ => break oid,
180 }
181 };
182 self.peeled = Some(peeled_id);
183 self.target = Target::Object(peeled_id);
184 Ok(peeled_id)
185 }
186 }
187 }
188
189 fn follow_to_object_in_place_packed(
190 &mut self,
191 store: &file::Store,
192 packed: Option<&packed::Buffer>,
193 ) -> Result<ObjectId, peel::to_object::Error> {
194 self.follow_to_object_packed(store, packed)
195 }
196
197 fn follow_to_object_packed(
198 &mut self,
199 store: &file::Store,
200 packed: Option<&packed::Buffer>,
201 ) -> Result<ObjectId, peel::to_object::Error> {
202 match self.target {
203 Target::Object(id) => Ok(id),
204 Target::Symbolic(_) => {
205 let mut seen = BTreeSet::new();
206 let cursor = &mut *self;
207 while let Some(next) = cursor.follow_packed(store, packed) {
208 let next = next?;
209 if seen.contains(&next.name) {
210 return Err(peel::to_object::Error::Cycle {
211 start_absolute: store.reference_path(cursor.name.as_ref()),
212 });
213 }
214 *cursor = next;
215 seen.insert(cursor.name.clone());
216 const MAX_REF_DEPTH: usize = 5;
217 if seen.len() == MAX_REF_DEPTH {
218 return Err(peel::to_object::Error::DepthLimitExceeded {
219 max_depth: MAX_REF_DEPTH,
220 });
221 }
222 }
223 let oid = self.target.try_id().expect("peeled ref").to_owned();
224 Ok(oid)
225 }
226 }
227 }
228
229 fn follow(&self, store: &file::Store) -> Option<Result<Reference, file::find::existing::Error>> {
230 let packed = match store
231 .assure_packed_refs_uptodate()
232 .map_err(|err| file::find::existing::Error::Find(file::find::Error::PackedOpen(err)))
233 {
234 Ok(packed) => packed,
235 Err(err) => return Some(Err(err)),
236 };
237 self.follow_packed(store, packed.as_ref().map(|b| &***b))
238 }
239
240 fn follow_packed(
241 &self,
242 store: &file::Store,
243 packed: Option<&packed::Buffer>,
244 ) -> Option<Result<Reference, file::find::existing::Error>> {
245 match &self.target {
246 Target::Object(_) => None,
247 Target::Symbolic(full_name) => match store.try_find_packed(full_name.as_ref(), packed) {
248 Ok(Some(next)) => Some(Ok(next)),
249 Ok(None) => Some(Err(file::find::existing::Error::NotFound {
250 name: full_name.to_path().to_owned(),
251 })),
252 Err(err) => Some(Err(file::find::existing::Error::Find(err))),
253 },
254 }
255 }
256}