1
- mod from_tree {
1
+ #[ allow( clippy:: empty_docs) ]
2
+ ///
3
+ pub mod from_tree {
2
4
use std:: collections:: VecDeque ;
3
5
4
6
use bstr:: { BStr , BString , ByteSlice , ByteVec } ;
@@ -10,6 +12,19 @@ mod from_tree {
10
12
Entry , PathStorage , State , Version ,
11
13
} ;
12
14
15
+ /// The error returned by [State::from_tree()].
16
+ #[ derive( Debug , thiserror:: Error ) ]
17
+ #[ allow( missing_docs) ]
18
+ pub enum Error {
19
+ #[ error( "The path \" {path}\" is invalid" ) ]
20
+ InvalidComponent {
21
+ path : BString ,
22
+ source : gix_validate:: path:: component:: Error ,
23
+ } ,
24
+ #[ error( transparent) ]
25
+ Traversal ( #[ from] gix_traverse:: tree:: breadthfirst:: Error ) ,
26
+ }
27
+
13
28
/// Initialization
14
29
impl State {
15
30
/// Return a new and empty in-memory index assuming the given `object_hash`.
@@ -32,23 +47,42 @@ mod from_tree {
32
47
}
33
48
/// Create an index [`State`] by traversing `tree` recursively, accessing sub-trees
34
49
/// with `objects`.
50
+ /// `validate` is used to determine which validations to perform on every path component we see.
35
51
///
36
52
/// **No extension data is currently produced**.
37
- pub fn from_tree < Find > ( tree : & gix_hash:: oid , objects : Find ) -> Result < Self , breadthfirst:: Error >
53
+ pub fn from_tree < Find > (
54
+ tree : & gix_hash:: oid ,
55
+ objects : Find ,
56
+ validate : gix_validate:: path:: component:: Options ,
57
+ ) -> Result < Self , Error >
38
58
where
39
59
Find : gix_object:: Find ,
40
60
{
41
61
let _span = gix_features:: trace:: coarse!( "gix_index::State::from_tree()" ) ;
42
62
let mut buf = Vec :: new ( ) ;
43
- let root = objects. find_tree_iter ( tree, & mut buf) ?;
44
- let mut delegate = CollectEntries :: new ( ) ;
45
- breadthfirst ( root, breadthfirst:: State :: default ( ) , & objects, & mut delegate) ?;
63
+ let root = objects
64
+ . find_tree_iter ( tree, & mut buf)
65
+ . map_err ( breadthfirst:: Error :: from) ?;
66
+ let mut delegate = CollectEntries :: new ( validate) ;
67
+ match breadthfirst ( root, breadthfirst:: State :: default ( ) , & objects, & mut delegate) {
68
+ Ok ( ( ) ) => { }
69
+ Err ( gix_traverse:: tree:: breadthfirst:: Error :: Cancelled ) => {
70
+ let ( path, err) = delegate
71
+ . invalid_path
72
+ . take ( )
73
+ . expect ( "cancellation only happens on validation error" ) ;
74
+ return Err ( Error :: InvalidComponent { path, source : err } ) ;
75
+ }
76
+ Err ( err) => return Err ( err. into ( ) ) ,
77
+ }
46
78
47
79
let CollectEntries {
48
80
mut entries,
49
81
path_backing,
50
82
path : _,
51
83
path_deque : _,
84
+ validate : _,
85
+ invalid_path : _,
52
86
} = delegate;
53
87
54
88
entries. sort_by ( |a, b| Entry :: cmp_filepaths ( a. path_in ( & path_backing) , b. path_in ( & path_backing) ) ) ;
@@ -76,15 +110,19 @@ mod from_tree {
76
110
path_backing : PathStorage ,
77
111
path : BString ,
78
112
path_deque : VecDeque < BString > ,
113
+ validate : gix_validate:: path:: component:: Options ,
114
+ invalid_path : Option < ( BString , gix_validate:: path:: component:: Error ) > ,
79
115
}
80
116
81
117
impl CollectEntries {
82
- pub fn new ( ) -> CollectEntries {
118
+ pub fn new ( validate : gix_validate :: path :: component :: Options ) -> CollectEntries {
83
119
CollectEntries {
84
120
entries : Vec :: new ( ) ,
85
121
path_backing : Vec :: new ( ) ,
86
122
path : BString :: default ( ) ,
87
123
path_deque : VecDeque :: new ( ) ,
124
+ validate,
125
+ invalid_path : None ,
88
126
}
89
127
}
90
128
@@ -93,6 +131,11 @@ mod from_tree {
93
131
self . path . push ( b'/' ) ;
94
132
}
95
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
+ }
96
139
}
97
140
98
141
pub fn add_entry ( & mut self , entry : & tree:: EntryRef < ' _ > ) {
@@ -103,6 +146,18 @@ mod from_tree {
103
146
EntryKind :: Link => Mode :: SYMLINK ,
104
147
EntryKind :: Commit => Mode :: COMMIT ,
105
148
} ;
149
+ // There are leaf-names that require special validation, specific to their mode.
150
+ // Double-validate just for this case, as the previous validation didn't know the mode yet.
151
+ 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
+ }
106
161
107
162
let path_start = self . path_backing . len ( ) ;
108
163
self . path_backing . extend_from_slice ( & self . path ) ;
@@ -117,6 +172,14 @@ mod from_tree {
117
172
118
173
self . entries . push ( new_entry) ;
119
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
+ }
120
183
}
121
184
122
185
impl Visit for CollectEntries {
@@ -127,12 +190,12 @@ mod from_tree {
127
190
. expect ( "every call is matched with push_tracked_path_component" ) ;
128
191
}
129
192
130
- fn push_back_tracked_path_component ( & mut self , component : & bstr :: BStr ) {
193
+ fn push_back_tracked_path_component ( & mut self , component : & BStr ) {
131
194
self . push_element ( component) ;
132
195
self . path_deque . push_back ( self . path . clone ( ) ) ;
133
196
}
134
197
135
- fn push_path_component ( & mut self , component : & bstr :: BStr ) {
198
+ fn push_path_component ( & mut self , component : & BStr ) {
136
199
self . push_element ( component) ;
137
200
}
138
201
@@ -144,13 +207,13 @@ mod from_tree {
144
207
}
145
208
}
146
209
147
- fn visit_tree ( & mut self , _entry : & gix_object:: tree:: EntryRef < ' _ > ) -> gix_traverse :: tree :: visit :: Action {
148
- Action :: Continue
210
+ fn visit_tree ( & mut self , _entry : & gix_object:: tree:: EntryRef < ' _ > ) -> Action {
211
+ self . determine_action ( )
149
212
}
150
213
151
- fn visit_nontree ( & mut self , entry : & gix_object:: tree:: EntryRef < ' _ > ) -> gix_traverse :: tree :: visit :: Action {
214
+ fn visit_nontree ( & mut self , entry : & gix_object:: tree:: EntryRef < ' _ > ) -> Action {
152
215
self . add_entry ( entry) ;
153
- Action :: Continue
216
+ self . determine_action ( )
154
217
}
155
218
}
156
219
}
0 commit comments