1use gix_error::ExnResult;
2pub trait Exists {
4 fn exists(&self, id: &gix_hash::oid) -> bool;
6}
7
8pub trait Find {
17 fn try_find<'a>(&self, id: &gix_hash::oid, buffer: &'a mut Vec<u8>) -> ExnResult<Option<crate::Data<'a>>>;
22}
23
24pub trait Header {
26 fn try_header(&self, id: &gix_hash::oid) -> ExnResult<Option<crate::Header>>;
30}
31
32pub trait FindObjectOrHeader: Find + Header {}
34
35mod _impls {
36 use std::{ops::Deref, rc::Rc, sync::Arc};
37
38 use gix_error::ExnResult;
39
40 use gix_hash::oid;
41
42 use crate::Data;
43
44 impl<T> crate::Exists for &T
45 where
46 T: crate::Exists,
47 {
48 fn exists(&self, id: &oid) -> bool {
49 (*self).exists(id)
50 }
51 }
52
53 impl<T> crate::FindObjectOrHeader for T where T: crate::Find + crate::FindHeader {}
54
55 impl<T> crate::Find for &T
56 where
57 T: crate::Find,
58 {
59 fn try_find<'a>(&self, id: &oid, buffer: &'a mut Vec<u8>) -> ExnResult<Option<Data<'a>>> {
60 (*self).try_find(id, buffer)
61 }
62 }
63
64 impl<T> crate::FindHeader for &T
65 where
66 T: crate::FindHeader,
67 {
68 fn try_header(&self, id: &gix_hash::oid) -> ExnResult<Option<crate::Header>> {
69 (*self).try_header(id)
70 }
71 }
72
73 impl<T> crate::Exists for Box<T>
74 where
75 T: crate::Exists,
76 {
77 fn exists(&self, id: &oid) -> bool {
78 self.deref().exists(id)
79 }
80 }
81
82 impl<T> crate::Exists for Rc<T>
83 where
84 T: crate::Exists,
85 {
86 fn exists(&self, id: &oid) -> bool {
87 self.deref().exists(id)
88 }
89 }
90
91 impl<T> crate::Find for Rc<T>
92 where
93 T: crate::Find,
94 {
95 fn try_find<'a>(&self, id: &oid, buffer: &'a mut Vec<u8>) -> ExnResult<Option<Data<'a>>> {
96 self.deref().try_find(id, buffer)
97 }
98 }
99
100 impl<T> crate::FindHeader for Rc<T>
101 where
102 T: crate::FindHeader,
103 {
104 fn try_header(&self, id: &gix_hash::oid) -> ExnResult<Option<crate::Header>> {
105 self.deref().try_header(id)
106 }
107 }
108
109 impl<T> crate::Find for Box<T>
110 where
111 T: crate::Find,
112 {
113 fn try_find<'a>(&self, id: &oid, buffer: &'a mut Vec<u8>) -> ExnResult<Option<Data<'a>>> {
114 self.deref().try_find(id, buffer)
115 }
116 }
117
118 impl<T> crate::FindHeader for Box<T>
119 where
120 T: crate::FindHeader,
121 {
122 fn try_header(&self, id: &gix_hash::oid) -> ExnResult<Option<crate::Header>> {
123 self.deref().try_header(id)
124 }
125 }
126
127 impl<T> crate::Exists for Arc<T>
128 where
129 T: crate::Exists,
130 {
131 fn exists(&self, id: &oid) -> bool {
132 self.deref().exists(id)
133 }
134 }
135
136 impl<T> crate::Find for Arc<T>
137 where
138 T: crate::Find,
139 {
140 fn try_find<'a>(&self, id: &oid, buffer: &'a mut Vec<u8>) -> ExnResult<Option<Data<'a>>> {
141 self.deref().try_find(id, buffer)
142 }
143 }
144
145 impl<T> crate::FindHeader for Arc<T>
146 where
147 T: crate::FindHeader,
148 {
149 fn try_header(&self, id: &gix_hash::oid) -> ExnResult<Option<crate::Header>> {
150 self.deref().try_header(id)
151 }
152 }
153}
154
155mod ext {
156 use gix_error::{ErrorExt, ExnResult, ResultExt, corruption, validation};
157
158 use crate::{BlobRef, CommitRef, CommitRefIter, Kind, ObjectRef, TagRef, TagRefIter, TreeRef, TreeRefIter};
159
160 fn not_found(id: &gix_hash::oid) -> gix_error::Exn {
161 gix_error::not_found(format!("An object with id {id} could not be found")).raise_erased()
162 }
163
164 fn wrong_kind(id: &gix_hash::oid, actual: Kind, expected: Kind) -> gix_error::Exn {
165 validation(format!("Expected object of kind {expected} but got {actual} at {id}")).raise_erased()
166 }
167
168 macro_rules! make_obj_lookup {
169 ($method:ident, $object_variant:path, $object_kind:path, $object_type:ty) => {
170 fn $method<'a>(&self, id: &gix_hash::oid, buffer: &'a mut Vec<u8>) -> ExnResult<$object_type> {
173 self.try_find(id, buffer)?
174 .ok_or_else(|| not_found(id))
175 .and_then(|o| {
176 o.decode()
177 .or_raise_erased(|| corruption(format!("Could not decode object at {id}")))
178 })
179 .and_then(|o| match o {
180 $object_variant(o) => return Ok(o),
181 o => Err(wrong_kind(id, o.kind(), $object_kind)),
182 })
183 }
184 };
185 }
186
187 macro_rules! make_iter_lookup {
188 ($method:ident, $object_kind:path, $object_type:ty, $into_iter:tt) => {
189 fn $method<'a>(&self, id: &gix_hash::oid, buffer: &'a mut Vec<u8>) -> ExnResult<$object_type> {
192 self.try_find(id, buffer)?
193 .ok_or_else(|| not_found(id))
194 .and_then(|o| o.$into_iter().ok_or_else(|| wrong_kind(id, o.kind, $object_kind)))
195 }
196 };
197 }
198
199 pub trait HeaderExt: super::Header {
201 fn header(&self, id: &gix_hash::oid) -> ExnResult<crate::Header> {
203 self.try_header(id)?.ok_or_else(|| not_found(id))
204 }
205 }
206
207 pub trait FindExt: super::Find {
209 fn find<'a>(&self, id: &gix_hash::oid, buffer: &'a mut Vec<u8>) -> ExnResult<crate::Data<'a>> {
211 self.try_find(id, buffer)?.ok_or_else(|| not_found(id))
212 }
213
214 fn find_blob<'a>(&self, id: &gix_hash::oid, buffer: &'a mut Vec<u8>) -> ExnResult<BlobRef<'a>> {
217 if id == gix_hash::ObjectId::empty_blob(id.kind()) {
218 return Ok(BlobRef { data: &[] });
219 }
220 self.try_find(id, buffer)?
221 .ok_or_else(|| not_found(id))
222 .and_then(|o| {
223 o.decode()
224 .or_raise_erased(|| corruption(format!("Could not decode object at {id}")))
225 })
226 .and_then(|o| match o {
227 ObjectRef::Blob(o) => Ok(o),
228 o => Err(wrong_kind(id, o.kind(), Kind::Blob)),
229 })
230 }
231
232 fn find_tree<'a>(&self, id: &gix_hash::oid, buffer: &'a mut Vec<u8>) -> ExnResult<TreeRef<'a>> {
235 if id == gix_hash::ObjectId::empty_tree(id.kind()) {
236 return Ok(TreeRef { entries: Vec::new() });
237 }
238 self.try_find(id, buffer)?
239 .ok_or_else(|| not_found(id))
240 .and_then(|o| {
241 o.decode()
242 .or_raise_erased(|| corruption(format!("Could not decode object at {id}")))
243 })
244 .and_then(|o| match o {
245 ObjectRef::Tree(o) => Ok(o),
246 o => Err(wrong_kind(id, o.kind(), Kind::Tree)),
247 })
248 }
249
250 make_obj_lookup!(find_commit, ObjectRef::Commit, Kind::Commit, CommitRef<'a>);
251 make_obj_lookup!(find_tag, ObjectRef::Tag, Kind::Tag, TagRef<'a>);
252 make_iter_lookup!(find_commit_iter, Kind::Commit, CommitRefIter<'a>, try_into_commit_iter);
253 make_iter_lookup!(find_tree_iter, Kind::Tree, TreeRefIter<'a>, try_into_tree_iter);
254 make_iter_lookup!(find_tag_iter, Kind::Tag, TagRefIter<'a>, try_into_tag_iter);
255 }
256
257 impl<T: super::Find + ?Sized> FindExt for T {}
258}
259pub use ext::{FindExt, HeaderExt};