1use std::io;
2
3use bstr::{BString, ByteSlice};
4
5use crate::{
6 encode::SPACE,
7 tree::{Entry, EntryRef},
8 Kind, Tree, TreeRef,
9};
10
11#[derive(Debug, thiserror::Error)]
13#[allow(missing_docs)]
14pub enum Error {
15 #[error("Nullbytes are invalid in file paths as they are separators: {name:?}")]
16 NullbyteInFilename { name: BString },
17}
18
19impl From<Error> for io::Error {
20 fn from(err: Error) -> Self {
21 io::Error::new(io::ErrorKind::Other, err)
22 }
23}
24
25impl crate::WriteTo for Tree {
27 fn write_to(&self, out: &mut dyn io::Write) -> io::Result<()> {
29 debug_assert_eq!(
30 &self.entries,
31 &{
32 let mut entries_sorted = self.entries.clone();
33 entries_sorted.sort();
34 entries_sorted
35 },
36 "entries for serialization must be sorted by filename"
37 );
38 let mut buf = Default::default();
39 for Entry { mode, filename, oid } in &self.entries {
40 out.write_all(mode.as_bytes(&mut buf))?;
41 out.write_all(SPACE)?;
42
43 if filename.find_byte(0).is_some() {
44 return Err(Error::NullbyteInFilename {
45 name: (*filename).to_owned(),
46 }
47 .into());
48 }
49 out.write_all(filename)?;
50 out.write_all(b"\0")?;
51
52 out.write_all(oid.as_bytes())?;
53 }
54 Ok(())
55 }
56
57 fn kind(&self) -> Kind {
58 Kind::Tree
59 }
60
61 fn size(&self) -> u64 {
62 let mut buf = Default::default();
63 self.entries
64 .iter()
65 .map(|Entry { mode, filename, oid }| {
66 (mode.as_bytes(&mut buf).len() + 1 + filename.len() + 1 + oid.as_bytes().len()) as u64
67 })
68 .sum()
69 }
70}
71
72impl crate::WriteTo for TreeRef<'_> {
74 fn write_to(&self, out: &mut dyn io::Write) -> io::Result<()> {
76 debug_assert_eq!(
77 &{
78 let mut entries_sorted = self.entries.clone();
79 entries_sorted.sort();
80 entries_sorted
81 },
82 &self.entries,
83 "entries for serialization must be sorted by filename"
84 );
85 let mut buf = Default::default();
86 for EntryRef { mode, filename, oid } in &self.entries {
87 out.write_all(mode.as_bytes(&mut buf))?;
88 out.write_all(SPACE)?;
89
90 if filename.find_byte(0).is_some() {
91 return Err(Error::NullbyteInFilename {
92 name: (*filename).to_owned(),
93 }
94 .into());
95 }
96 out.write_all(filename)?;
97 out.write_all(b"\0")?;
98
99 out.write_all(oid.as_bytes())?;
100 }
101 Ok(())
102 }
103
104 fn kind(&self) -> Kind {
105 Kind::Tree
106 }
107
108 fn size(&self) -> u64 {
109 let mut buf = Default::default();
110 self.entries
111 .iter()
112 .map(|EntryRef { mode, filename, oid }| {
113 (mode.as_bytes(&mut buf).len() + 1 + filename.len() + 1 + oid.as_bytes().len()) as u64
114 })
115 .sum()
116 }
117}