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