gix_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("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
25/// Serialization
26impl crate::WriteTo for Tree {
27    /// Serialize this tree to `out` in the git internal format.
28    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
72/// Serialization
73impl crate::WriteTo for TreeRef<'_> {
74    /// Serialize this tree to `out` in the git internal format.
75    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}