gix_ref/store/file/
raw_ext.rs1use std::collections::BTreeSet;
2
3use crate::{
4 packed, peel,
5 raw::Reference,
6 store_impl::{file, file::log},
7 Target,
8};
9use gix_hash::ObjectId;
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 fn peel_to_id_in_place(
29 &mut self,
30 store: &file::Store,
31 objects: &dyn gix_object::Find,
32 ) -> Result<ObjectId, peel::to_id::Error>;
33
34 fn peel_to_id_in_place_packed(
37 &mut self,
38 store: &file::Store,
39 objects: &dyn gix_object::Find,
40 packed: Option<&packed::Buffer>,
41 ) -> Result<ObjectId, peel::to_id::Error>;
42
43 fn follow_to_object_in_place_packed(
46 &mut self,
47 store: &file::Store,
48 packed: Option<&packed::Buffer>,
49 ) -> Result<ObjectId, peel::to_object::Error>;
50
51 fn follow(&self, store: &file::Store) -> Option<Result<Reference, file::find::existing::Error>>;
55
56 fn follow_packed(
61 &self,
62 store: &file::Store,
63 packed: Option<&packed::Buffer>,
64 ) -> Option<Result<Reference, file::find::existing::Error>>;
65}
66
67impl ReferenceExt for Reference {
68 fn log_iter<'a, 's>(&'a self, store: &'s file::Store) -> log::iter::Platform<'a, 's> {
69 log::iter::Platform {
70 store,
71 name: self.name.as_ref(),
72 buf: Vec::new(),
73 }
74 }
75
76 fn log_exists(&self, store: &file::Store) -> bool {
77 store
78 .reflog_exists(self.name.as_ref())
79 .expect("infallible name conversion")
80 }
81
82 fn peel_to_id_in_place(
83 &mut self,
84 store: &file::Store,
85 objects: &dyn gix_object::Find,
86 ) -> Result<ObjectId, peel::to_id::Error> {
87 let packed = store.assure_packed_refs_uptodate().map_err(|err| {
88 peel::to_id::Error::FollowToObject(peel::to_object::Error::Follow(file::find::existing::Error::Find(
89 file::find::Error::PackedOpen(err),
90 )))
91 })?;
92 self.peel_to_id_in_place_packed(store, objects, packed.as_ref().map(|b| &***b))
93 }
94
95 fn peel_to_id_in_place_packed(
96 &mut self,
97 store: &file::Store,
98 objects: &dyn gix_object::Find,
99 packed: Option<&packed::Buffer>,
100 ) -> Result<ObjectId, peel::to_id::Error> {
101 match self.peeled {
102 Some(peeled) => {
103 self.target = Target::Object(peeled.to_owned());
104 Ok(peeled)
105 }
106 None => {
107 let mut oid = self.follow_to_object_in_place_packed(store, packed)?;
108 let mut buf = Vec::new();
109 let peeled_id = loop {
110 let gix_object::Data { kind, data } =
111 objects
112 .try_find(&oid, &mut buf)?
113 .ok_or_else(|| peel::to_id::Error::NotFound {
114 oid,
115 name: self.name.0.clone(),
116 })?;
117 match kind {
118 gix_object::Kind::Tag => {
119 oid = gix_object::TagRefIter::from_bytes(data).target_id().map_err(|_err| {
120 peel::to_id::Error::NotFound {
121 oid,
122 name: self.name.0.clone(),
123 }
124 })?;
125 }
126 _ => break oid,
127 };
128 };
129 self.peeled = Some(peeled_id);
130 self.target = Target::Object(peeled_id);
131 Ok(peeled_id)
132 }
133 }
134 }
135
136 fn follow_to_object_in_place_packed(
137 &mut self,
138 store: &file::Store,
139 packed: Option<&packed::Buffer>,
140 ) -> Result<ObjectId, peel::to_object::Error> {
141 match self.target {
142 Target::Object(id) => Ok(id),
143 Target::Symbolic(_) => {
144 let mut seen = BTreeSet::new();
145 let cursor = &mut *self;
146 while let Some(next) = cursor.follow_packed(store, packed) {
147 let next = next?;
148 if seen.contains(&next.name) {
149 return Err(peel::to_object::Error::Cycle {
150 start_absolute: store.reference_path(cursor.name.as_ref()),
151 });
152 }
153 *cursor = next;
154 seen.insert(cursor.name.clone());
155 const MAX_REF_DEPTH: usize = 5;
156 if seen.len() == MAX_REF_DEPTH {
157 return Err(peel::to_object::Error::DepthLimitExceeded {
158 max_depth: MAX_REF_DEPTH,
159 });
160 }
161 }
162 let oid = self.target.try_id().expect("peeled ref").to_owned();
163 Ok(oid)
164 }
165 }
166 }
167
168 fn follow(&self, store: &file::Store) -> Option<Result<Reference, file::find::existing::Error>> {
169 let packed = match store
170 .assure_packed_refs_uptodate()
171 .map_err(|err| file::find::existing::Error::Find(file::find::Error::PackedOpen(err)))
172 {
173 Ok(packed) => packed,
174 Err(err) => return Some(Err(err)),
175 };
176 self.follow_packed(store, packed.as_ref().map(|b| &***b))
177 }
178
179 fn follow_packed(
180 &self,
181 store: &file::Store,
182 packed: Option<&packed::Buffer>,
183 ) -> Option<Result<Reference, file::find::existing::Error>> {
184 match &self.target {
185 Target::Object(_) => None,
186 Target::Symbolic(full_name) => match store.try_find_packed(full_name.as_ref(), packed) {
187 Ok(Some(next)) => Some(Ok(next)),
188 Ok(None) => Some(Err(file::find::existing::Error::NotFound {
189 name: full_name.to_path().to_owned(),
190 })),
191 Err(err) => Some(Err(file::find::existing::Error::Find(err))),
192 },
193 }
194 }
195}