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