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("Newlines are invalid in file paths: {name:?}")]
16 NewlineInFilename { 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, mut out: impl io::Write) -> io::Result<()> {
29 debug_assert_eq!(
30 &{
31 let mut entries_sorted = self.entries.clone();
32 entries_sorted.sort();
33 entries_sorted
34 },
35 &self.entries,
36 "entries for serialization must be sorted by filename"
37 );
38 for Entry { mode, filename, oid } in &self.entries {
39 out.write_all(mode.as_bytes())?;
40 out.write_all(SPACE)?;
41
42 if filename.find_byte(b'\n').is_some() {
43 return Err(Error::NewlineInFilename {
44 name: (*filename).to_owned(),
45 }
46 .into());
47 }
48 out.write_all(filename)?;
49 out.write_all(&[b'\0'])?;
50
51 out.write_all(oid.as_bytes())?;
52 }
53 Ok(())
54 }
55
56 fn size(&self) -> usize {
57 self.entries
58 .iter()
59 .map(|Entry { mode, filename, oid }| mode.as_bytes().len() + 1 + filename.len() + 1 + oid.as_bytes().len())
60 .sum()
61 }
62
63 fn kind(&self) -> Kind {
64 Kind::Tree
65 }
66}
67
68impl<'a> crate::WriteTo for TreeRef<'a> {
70 fn write_to(&self, mut out: impl io::Write) -> io::Result<()> {
72 debug_assert_eq!(
73 &{
74 let mut entries_sorted = self.entries.clone();
75 entries_sorted.sort();
76 entries_sorted
77 },
78 &self.entries,
79 "entries for serialization must be sorted by filename"
80 );
81 for EntryRef { mode, filename, oid } in &self.entries {
82 out.write_all(mode.as_bytes())?;
83 out.write_all(SPACE)?;
84
85 if filename.find_byte(b'\n').is_some() {
86 return Err(Error::NewlineInFilename {
87 name: (*filename).to_owned(),
88 }
89 .into());
90 }
91 out.write_all(filename)?;
92 out.write_all(&[b'\0'])?;
93
94 out.write_all(oid.as_bytes())?;
95 }
96 Ok(())
97 }
98
99 fn size(&self) -> usize {
100 self.entries
101 .iter()
102 .map(|EntryRef { mode, filename, oid }| {
103 mode.as_bytes().len() + 1 + filename.len() + 1 + oid.as_bytes().len()
104 })
105 .sum()
106 }
107
108 fn kind(&self) -> Kind {
109 Kind::Tree
110 }
111}