objects/object/
tree_struct.rs1use std::path::Path;
5
6use serde::{Deserialize, Serialize};
7
8use super::{ContentHash, TreeEntry, TreeError};
9
10#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
12pub struct Tree {
13 entries: Vec<TreeEntry>,
14}
15
16impl Tree {
17 pub fn new() -> Self {
18 Self {
19 entries: Vec::new(),
20 }
21 }
22
23 pub fn from_entries(mut entries: Vec<TreeEntry>) -> Self {
24 entries.sort_by(|a, b| a.name.cmp(&b.name));
25 Self { entries }
26 }
27
28 pub fn validate(&self) -> Result<(), TreeError> {
29 let mut previous_name: Option<&str> = None;
30
31 for entry in &self.entries {
32 entry.validate()?;
33
34 if let Some(previous) = previous_name
35 && previous >= entry.name.as_str()
36 {
37 return Err(TreeError::InvalidStructure(
38 "entries must be strictly sorted by name".to_string(),
39 ));
40 }
41
42 previous_name = Some(&entry.name);
43 }
44
45 Ok(())
46 }
47
48 pub fn entries(&self) -> &[TreeEntry] {
49 &self.entries
50 }
51
52 pub fn get(&self, name: &str) -> Option<&TreeEntry> {
53 let index = self
54 .entries
55 .binary_search_by(|entry| entry.name.as_str().cmp(name))
56 .ok()?;
57 self.entries.get(index)
58 }
59
60 pub fn insert(&mut self, entry: TreeEntry) {
61 self.entries.retain(|e| e.name != entry.name);
62
63 let pos = self
64 .entries
65 .iter()
66 .position(|e| e.name > entry.name)
67 .unwrap_or(self.entries.len());
68 self.entries.insert(pos, entry);
69 }
70
71 pub fn remove(&mut self, name: &str) -> Option<TreeEntry> {
72 let pos = self.entries.iter().position(|e| e.name == name)?;
73 Some(self.entries.remove(pos))
74 }
75
76 pub fn is_empty(&self) -> bool {
77 self.entries.is_empty()
78 }
79
80 pub fn len(&self) -> usize {
81 self.entries.len()
82 }
83
84 pub fn hash(&self) -> ContentHash {
85 let total_len: usize = self.entries.iter().map(TreeEntry::encoded_len).sum();
86 ContentHash::compute_typed_with_len("tree", total_len as u64, |hasher| {
87 for entry in &self.entries {
88 entry.update_hasher(hasher);
89 }
90 })
91 }
92
93 pub fn iter(&self) -> impl Iterator<Item = &TreeEntry> {
94 self.entries.iter()
95 }
96
97 pub fn get_path(&self, path: &Path) -> Option<&TreeEntry> {
98 let name = path.file_name()?.to_str()?;
99 if path.parent().is_none_or(|p| p.as_os_str().is_empty()) {
100 self.get(name)
101 } else {
102 None
103 }
104 }
105}
106
107impl Default for Tree {
108 fn default() -> Self {
109 Self::new()
110 }
111}
112
113impl IntoIterator for Tree {
114 type Item = TreeEntry;
115 type IntoIter = std::vec::IntoIter<TreeEntry>;
116
117 fn into_iter(self) -> Self::IntoIter {
118 self.entries.into_iter()
119 }
120}
121
122impl<'a> IntoIterator for &'a Tree {
123 type Item = &'a TreeEntry;
124 type IntoIter = std::slice::Iter<'a, TreeEntry>;
125
126 fn into_iter(self) -> Self::IntoIter {
127 self.entries.iter()
128 }
129}