@@ -184,13 +184,112 @@ impl Component {
184184 // TODO: If this is the last component remove the components file
185185 // and the version file.
186186
187+ // Track visited directories
188+ use std:: collections:: HashSet ;
189+ use std:: collections:: hash_set:: IntoIter ;
190+ use std:: fs:: read_dir;
191+
192+ // dirs will contain the set of longest disjoint directory paths seen
193+ // ancestors help in filtering seen paths and constructing dirs
194+ // All seen paths must be relative to avoid surprises
195+ struct PruneSet {
196+ dirs : HashSet < PathBuf > ,
197+ ancestors : HashSet < PathBuf > ,
198+ prefix : PathBuf ,
199+ }
200+
201+ impl PruneSet {
202+ fn seen ( & mut self , mut path : PathBuf ) {
203+ if !path. is_relative ( ) || !path. pop ( ) {
204+ return ;
205+ }
206+ if self . dirs . contains ( & path) || self . ancestors . contains ( & path) {
207+ return ;
208+ }
209+ self . dirs . insert ( path. clone ( ) ) ;
210+ while path. pop ( ) {
211+ if path. file_name ( ) . is_none ( ) {
212+ break ;
213+ }
214+ if self . dirs . contains ( & path) {
215+ self . dirs . remove ( & path) ;
216+ }
217+ if self . ancestors . contains ( & path) {
218+ break ;
219+ }
220+ self . ancestors . insert ( path. clone ( ) ) ;
221+ }
222+ }
223+ }
224+
225+ struct PruneIter {
226+ iter : IntoIter < PathBuf > ,
227+ path_buf : Option < PathBuf > ,
228+ prefix : PathBuf ,
229+ }
230+
231+ impl IntoIterator for PruneSet {
232+ type Item = PathBuf ;
233+ type IntoIter = PruneIter ;
234+
235+ fn into_iter ( self ) -> Self :: IntoIter {
236+ PruneIter {
237+ iter : self . dirs . into_iter ( ) ,
238+ path_buf : None ,
239+ prefix : self . prefix ,
240+ }
241+ }
242+ }
243+
244+ // Returns only empty directories
245+ impl Iterator for PruneIter {
246+ type Item = PathBuf ;
247+
248+ fn next ( & mut self ) -> Option < Self :: Item > {
249+ self . path_buf = match self . path_buf {
250+ None => self . iter . next ( ) ,
251+ Some ( _) => {
252+ let mut path_buf = self . path_buf . take ( ) . unwrap ( ) ;
253+ match path_buf. file_name ( ) {
254+ Some ( _) => if path_buf. pop ( ) { Some ( path_buf) } else { None } ,
255+ None => self . iter . next ( ) ,
256+ }
257+ } ,
258+ } ;
259+ if self . path_buf . is_none ( ) {
260+ return None ;
261+ }
262+ let full_path = self . prefix . join ( self . path_buf . as_ref ( ) . unwrap ( ) ) ;
263+ let empty = match read_dir ( full_path) {
264+ Ok ( dir) => dir. count ( ) == 0 ,
265+ Err ( _) => false ,
266+ } ;
267+ if empty {
268+ self . path_buf . clone ( )
269+ } else {
270+ // No dir above can be empty, go to next path in dirs
271+ self . path_buf = None ;
272+ self . next ( )
273+ }
274+ }
275+ }
276+
187277 // Remove parts
278+ let mut pset = PruneSet {
279+ dirs : HashSet :: new ( ) ,
280+ ancestors : HashSet :: new ( ) ,
281+ prefix : self . components . prefix . abs_path ( "" ) ,
282+ } ;
188283 for part in try!( self . parts ( ) ) . into_iter ( ) . rev ( ) {
189284 match & * part. 0 {
190- "file" => try!( tx. remove_file ( & self . name , part. 1 ) ) ,
191- "dir" => try!( tx. remove_dir ( & self . name , part. 1 ) ) ,
285+ "file" => try!( tx. remove_file ( & self . name , part. 1 . clone ( ) ) ) ,
286+ "dir" => try!( tx. remove_dir ( & self . name , part. 1 . clone ( ) ) ) ,
192287 _ => return Err ( ErrorKind :: CorruptComponent ( self . name . clone ( ) ) . into ( ) ) ,
193288 }
289+ pset. seen ( part. 1 ) ;
290+ }
291+ for empty_dir in pset {
292+ try!( tx. remove_dir ( & self . name , empty_dir) ) ;
194293 }
195294
196295 // Remove component manifest
0 commit comments