1pub mod from_tree {
3 use std::collections::VecDeque;
4
5 use bstr::{BStr, BString, ByteSlice, ByteVec};
6 use gix_object::{tree, tree::EntryKind};
7 use gix_traverse::tree::{depthfirst, visit::Action, Visit};
8
9 use crate::{
10 entry::{Flags, Mode, Stat},
11 Entry, PathStorage, State, Version,
12 };
13
14 #[derive(Debug, thiserror::Error)]
16 #[allow(missing_docs)]
17 pub enum Error {
18 #[error("The path \"{path}\" is invalid")]
19 InvalidComponent {
20 path: BString,
21 source: gix_validate::path::component::Error,
22 },
23 #[error(transparent)]
24 Traversal(#[from] gix_traverse::tree::depthfirst::Error),
25 }
26
27 impl State {
29 pub fn new(object_hash: gix_hash::Kind) -> Self {
31 State {
32 object_hash,
33 timestamp: filetime::FileTime::now(),
34 version: Version::V2,
35 entries: vec![],
36 path_backing: vec![],
37 is_sparse: false,
38 tree: None,
39 link: None,
40 resolve_undo: None,
41 untracked: None,
42 fs_monitor: None,
43 offset_table_at_decode_time: false,
44 end_of_index_at_decode_time: false,
45 }
46 }
47 pub fn from_tree<Find>(
53 tree: &gix_hash::oid,
54 objects: Find,
55 validate: gix_validate::path::component::Options,
56 ) -> Result<Self, Error>
57 where
58 Find: gix_object::Find,
59 {
60 let _span = gix_features::trace::coarse!("gix_index::State::from_tree()");
61 let mut delegate = CollectEntries::new(validate);
62 match depthfirst(tree.to_owned(), depthfirst::State::default(), &objects, &mut delegate) {
63 Ok(()) => {}
64 Err(gix_traverse::tree::breadthfirst::Error::Cancelled) => {
65 let (path, err) = delegate
66 .invalid_path
67 .take()
68 .expect("cancellation only happens on validation error");
69 return Err(Error::InvalidComponent { path, source: err });
70 }
71 Err(err) => return Err(err.into()),
72 }
73
74 let CollectEntries {
75 entries,
76 path_backing,
77 path: _,
78 path_deque: _,
79 validate: _,
80 invalid_path,
81 } = delegate;
82
83 if let Some((path, err)) = invalid_path {
84 return Err(Error::InvalidComponent { path, source: err });
85 }
86
87 Ok(State {
88 object_hash: tree.kind(),
89 timestamp: filetime::FileTime::now(),
90 version: Version::V2,
91 entries,
92 path_backing,
93 is_sparse: false,
94 tree: None,
95 link: None,
96 resolve_undo: None,
97 untracked: None,
98 fs_monitor: None,
99 offset_table_at_decode_time: false,
100 end_of_index_at_decode_time: false,
101 })
102 }
103 }
104
105 struct CollectEntries {
106 entries: Vec<Entry>,
107 path_backing: PathStorage,
108 path: BString,
109 path_deque: VecDeque<BString>,
110 validate: gix_validate::path::component::Options,
111 invalid_path: Option<(BString, gix_validate::path::component::Error)>,
112 }
113
114 impl CollectEntries {
115 pub fn new(validate: gix_validate::path::component::Options) -> CollectEntries {
116 CollectEntries {
117 entries: Vec::new(),
118 path_backing: Vec::new(),
119 path: BString::default(),
120 path_deque: VecDeque::new(),
121 validate,
122 invalid_path: None,
123 }
124 }
125
126 fn push_element(&mut self, name: &BStr) {
127 if name.is_empty() {
128 return;
129 }
130 if !self.path.is_empty() {
131 self.path.push(b'/');
132 }
133 self.path.push_str(name);
134 if self.invalid_path.is_none() {
135 if let Err(err) = gix_validate::path::component(name, None, self.validate) {
136 self.invalid_path = Some((self.path.clone(), err));
137 }
138 }
139 }
140
141 pub fn add_entry(&mut self, entry: &tree::EntryRef<'_>) {
142 let mode = match entry.mode.kind() {
143 EntryKind::Tree => unreachable!("visit_non_tree() called us"),
144 EntryKind::Blob => Mode::FILE,
145 EntryKind::BlobExecutable => Mode::FILE_EXECUTABLE,
146 EntryKind::Link => Mode::SYMLINK,
147 EntryKind::Commit => Mode::COMMIT,
148 };
149 if self.invalid_path.is_none() {
152 let start = self.path.rfind_byte(b'/').map(|pos| pos + 1).unwrap_or_default();
153 if let Err(err) = gix_validate::path::component(
154 self.path[start..].as_ref(),
155 (entry.mode.kind() == EntryKind::Link).then_some(gix_validate::path::component::Mode::Symlink),
156 self.validate,
157 ) {
158 self.invalid_path = Some((self.path.clone(), err));
159 }
160 }
161
162 let path_start = self.path_backing.len();
163 self.path_backing.extend_from_slice(&self.path);
164
165 let new_entry = Entry {
166 stat: Stat::default(),
167 id: entry.oid.into(),
168 flags: Flags::empty(),
169 mode,
170 path: path_start..self.path_backing.len(),
171 };
172
173 self.entries.push(new_entry);
174 }
175
176 fn determine_action(&self) -> Action {
177 if self.invalid_path.is_none() {
178 Action::Continue
179 } else {
180 Action::Cancel
181 }
182 }
183 }
184
185 impl Visit for CollectEntries {
186 fn pop_back_tracked_path_and_set_current(&mut self) {
187 self.path = self.path_deque.pop_back().unwrap_or_default();
188 }
189
190 fn pop_front_tracked_path_and_set_current(&mut self) {
191 self.path = self
192 .path_deque
193 .pop_front()
194 .expect("every call is matched with push_tracked_path_component");
195 }
196
197 fn push_back_tracked_path_component(&mut self, component: &BStr) {
198 self.push_element(component);
199 self.path_deque.push_back(self.path.clone());
200 }
201
202 fn push_path_component(&mut self, component: &BStr) {
203 self.push_element(component);
204 }
205
206 fn pop_path_component(&mut self) {
207 if let Some(pos) = self.path.rfind_byte(b'/') {
208 self.path.resize(pos, 0);
209 } else {
210 self.path.clear();
211 }
212 }
213
214 fn visit_tree(&mut self, _entry: &gix_object::tree::EntryRef<'_>) -> Action {
215 self.determine_action()
216 }
217
218 fn visit_nontree(&mut self, entry: &gix_object::tree::EntryRef<'_>) -> Action {
219 self.add_entry(entry);
220 self.determine_action()
221 }
222 }
223}