1use crate::{Blob, Commit, Object, Tag, Tree};
2
3mod convert;
4
5mod write {
6 use std::io;
7
8 use crate::{Kind, Object, ObjectRef, WriteTo};
9
10 impl WriteTo for ObjectRef<'_> {
12 fn write_to(&self, out: &mut dyn io::Write) -> io::Result<()> {
14 use crate::ObjectRef::*;
15 match self {
16 Tree(v) => v.write_to(out),
17 Blob(v) => v.write_to(out),
18 Commit(v) => v.write_to(out),
19 Tag(v) => v.write_to(out),
20 }
21 }
22
23 fn kind(&self) -> Kind {
24 self.kind()
25 }
26
27 fn size(&self) -> u64 {
28 use crate::ObjectRef::*;
29 match self {
30 Tree(v) => v.size(),
31 Blob(v) => v.size(),
32 Commit(v) => v.size(),
33 Tag(v) => v.size(),
34 }
35 }
36 }
37
38 impl WriteTo for Object {
40 fn write_to(&self, out: &mut dyn io::Write) -> io::Result<()> {
42 use crate::Object::*;
43 match self {
44 Tree(v) => v.write_to(out),
45 Blob(v) => v.write_to(out),
46 Commit(v) => v.write_to(out),
47 Tag(v) => v.write_to(out),
48 }
49 }
50
51 fn kind(&self) -> Kind {
52 self.kind()
53 }
54
55 fn size(&self) -> u64 {
56 use crate::Object::*;
57 match self {
58 Tree(v) => v.size(),
59 Blob(v) => v.size(),
60 Commit(v) => v.size(),
61 Tag(v) => v.size(),
62 }
63 }
64 }
65}
66
67impl Object {
69 pub fn into_blob(self) -> Blob {
71 match self {
72 Object::Blob(v) => v,
73 _ => panic!("BUG: not a blob"),
74 }
75 }
76 pub fn into_commit(self) -> Commit {
78 match self {
79 Object::Commit(v) => v,
80 _ => panic!("BUG: not a commit"),
81 }
82 }
83 pub fn into_tree(self) -> Tree {
85 match self {
86 Object::Tree(v) => v,
87 _ => panic!("BUG: not a tree"),
88 }
89 }
90 pub fn into_tag(self) -> Tag {
92 match self {
93 Object::Tag(v) => v,
94 _ => panic!("BUG: not a tag"),
95 }
96 }
97 #[expect(
99 clippy::result_large_err,
100 reason = "will be removed once `gix-error` is used consistently"
101 )]
102 pub fn try_into_blob(self) -> Result<Blob, Self> {
103 match self {
104 Object::Blob(v) => Ok(v),
105 _ => Err(self),
106 }
107 }
108 pub fn try_into_blob_ref(&self) -> Option<BlobRef<'_>> {
110 match self {
111 Object::Blob(v) => Some(v.to_ref()),
112 _ => None,
113 }
114 }
115 #[expect(
117 clippy::result_large_err,
118 reason = "will be removed once `gix-error` is used consistently"
119 )]
120 pub fn try_into_commit(self) -> Result<Commit, Self> {
121 match self {
122 Object::Commit(v) => Ok(v),
123 _ => Err(self),
124 }
125 }
126 #[expect(
128 clippy::result_large_err,
129 reason = "will be removed once `gix-error` is used consistently"
130 )]
131 pub fn try_into_tree(self) -> Result<Tree, Self> {
132 match self {
133 Object::Tree(v) => Ok(v),
134 _ => Err(self),
135 }
136 }
137 #[expect(
139 clippy::result_large_err,
140 reason = "will be removed once `gix-error` is used consistently"
141 )]
142 pub fn try_into_tag(self) -> Result<Tag, Self> {
143 match self {
144 Object::Tag(v) => Ok(v),
145 _ => Err(self),
146 }
147 }
148
149 pub fn as_blob(&self) -> Option<&Blob> {
151 match self {
152 Object::Blob(v) => Some(v),
153 _ => None,
154 }
155 }
156 pub fn as_commit(&self) -> Option<&Commit> {
158 match self {
159 Object::Commit(v) => Some(v),
160 _ => None,
161 }
162 }
163 pub fn as_tree(&self) -> Option<&Tree> {
165 match self {
166 Object::Tree(v) => Some(v),
167 _ => None,
168 }
169 }
170 pub fn as_tag(&self) -> Option<&Tag> {
172 match self {
173 Object::Tag(v) => Some(v),
174 _ => None,
175 }
176 }
177 pub fn kind(&self) -> crate::Kind {
179 match self {
180 Object::Tree(_) => crate::Kind::Tree,
181 Object::Blob(_) => crate::Kind::Blob,
182 Object::Commit(_) => crate::Kind::Commit,
183 Object::Tag(_) => crate::Kind::Tag,
184 }
185 }
186}
187
188use crate::{
189 BlobRef, CommitRef, Kind, ObjectRef, TagRef, TreeRef,
190 decode::{Error as DecodeError, LooseHeaderDecodeError, loose_header},
191};
192
193#[derive(Debug, thiserror::Error)]
194pub enum LooseDecodeError {
195 #[error(transparent)]
196 InvalidHeader(#[from] LooseHeaderDecodeError),
197 #[error(transparent)]
198 InvalidContent(#[from] DecodeError),
199 #[error("Object sized {size} does not fit into memory - this can happen on 32 bit systems")]
200 OutOfMemory { size: u64 },
201}
202
203impl<'a> ObjectRef<'a> {
204 pub fn from_loose(data: &'a [u8], hash_kind: gix_hash::Kind) -> Result<ObjectRef<'a>, LooseDecodeError> {
206 let (kind, size, offset) = loose_header(data)?;
207
208 let body = &data[offset..]
209 .get(..size.try_into().map_err(|_| LooseDecodeError::OutOfMemory { size })?)
210 .ok_or(LooseHeaderDecodeError::InvalidHeader {
211 message: "object data was shorter than its size declared in the header",
212 })?;
213
214 Ok(Self::from_bytes(body, kind, hash_kind)?)
215 }
216
217 pub fn from_bytes(
219 data: &'a [u8],
220 kind: Kind,
221 hash_kind: gix_hash::Kind,
222 ) -> Result<ObjectRef<'a>, crate::decode::Error> {
223 Ok(match kind {
224 Kind::Tree => ObjectRef::Tree(TreeRef::from_bytes(data, hash_kind)?),
225 Kind::Blob => ObjectRef::Blob(BlobRef { data }),
226 Kind::Commit => ObjectRef::Commit(CommitRef::from_bytes(data, hash_kind)?),
227 Kind::Tag => ObjectRef::Tag(TagRef::from_bytes(data, hash_kind)?),
228 })
229 }
230
231 pub fn into_owned(self) -> Result<Object, crate::decode::Error> {
235 self.try_into()
236 }
237
238 pub fn to_owned(&self) -> Result<Object, crate::decode::Error> {
242 self.clone().try_into()
243 }
244}
245
246impl<'a> ObjectRef<'a> {
248 pub fn as_blob(&self) -> Option<&BlobRef<'a>> {
250 match self {
251 ObjectRef::Blob(v) => Some(v),
252 _ => None,
253 }
254 }
255 pub fn into_blob(self) -> Option<BlobRef<'a>> {
257 match self {
258 ObjectRef::Blob(v) => Some(v),
259 _ => None,
260 }
261 }
262 pub fn as_commit(&self) -> Option<&CommitRef<'a>> {
264 match self {
265 ObjectRef::Commit(v) => Some(v),
266 _ => None,
267 }
268 }
269 pub fn into_commit(self) -> Option<CommitRef<'a>> {
271 match self {
272 ObjectRef::Commit(v) => Some(v),
273 _ => None,
274 }
275 }
276 pub fn as_tree(&self) -> Option<&TreeRef<'a>> {
278 match self {
279 ObjectRef::Tree(v) => Some(v),
280 _ => None,
281 }
282 }
283 pub fn into_tree(self) -> Option<TreeRef<'a>> {
285 match self {
286 ObjectRef::Tree(v) => Some(v),
287 _ => None,
288 }
289 }
290 pub fn as_tag(&self) -> Option<&TagRef<'a>> {
292 match self {
293 ObjectRef::Tag(v) => Some(v),
294 _ => None,
295 }
296 }
297 pub fn into_tag(self) -> Option<TagRef<'a>> {
299 match self {
300 ObjectRef::Tag(v) => Some(v),
301 _ => None,
302 }
303 }
304 pub fn kind(&self) -> Kind {
306 match self {
307 ObjectRef::Tree(_) => Kind::Tree,
308 ObjectRef::Blob(_) => Kind::Blob,
309 ObjectRef::Commit(_) => Kind::Commit,
310 ObjectRef::Tag(_) => Kind::Tag,
311 }
312 }
313}