git_object/tree/
write.rs

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/// The Error used in [`Tree::write_to()`][crate::WriteTo::write_to()].
12#[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
25/// Serialization
26impl crate::WriteTo for Tree {
27    /// Serialize this tree to `out` in the git internal format.
28    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
68/// Serialization
69impl<'a> crate::WriteTo for TreeRef<'a> {
70    /// Serialize this tree to `out` in the git internal format.
71    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}