@@ -8,7 +8,6 @@ use super::summary::{parse_summary, Link, SectionNumber, Summary, SummaryItem};
8
8
use config:: BuildConfig ;
9
9
use errors:: * ;
10
10
11
-
12
11
/// Load a book into memory from its `src/` directory.
13
12
pub fn load_book < P : AsRef < Path > > ( src_dir : P , cfg : & BuildConfig ) -> Result < Book > {
14
13
let src_dir = src_dir. as_ref ( ) ;
@@ -60,14 +59,19 @@ fn create_missing(src_dir: &Path, summary: &Summary) -> Result<()> {
60
59
Ok ( ( ) )
61
60
}
62
61
63
-
64
62
/// A dumb tree structure representing a book.
65
63
///
66
- /// For the moment a book is just a collection of `BookItems`.
64
+ /// For the moment a book is just a collection of `BookItems` which are
65
+ /// accessible by either iterating (immutably) over the book with [`iter()`], or
66
+ /// recursively applying a closure to each section to mutate the chapters, using
67
+ /// [`for_each_mut()`].
68
+ ///
69
+ /// [`iter()`]: #method.iter
70
+ /// [`for_each_mut()`]: #method.for_each_mut
67
71
#[ derive( Debug , Clone , Default , PartialEq , Serialize , Deserialize ) ]
68
72
pub struct Book {
69
73
/// The sections in this book.
70
- pub sections : Vec < BookItem > ,
74
+ sections : Vec < BookItem > ,
71
75
}
72
76
73
77
impl Book {
@@ -82,6 +86,35 @@ impl Book {
82
86
items : self . sections . iter ( ) . collect ( ) ,
83
87
}
84
88
}
89
+
90
+ /// Recursively apply a closure to each item in the book, allowing you to
91
+ /// mutate them.
92
+ ///
93
+ /// # Note
94
+ ///
95
+ /// Unlike the `iter()` method, this requires a closure instead of returning
96
+ /// an iterator. This is because using iterators can possibly allow you
97
+ /// to have iterator invalidation errors.
98
+ pub fn for_each_mut < F > ( & mut self , mut func : F )
99
+ where
100
+ F : FnMut ( & mut BookItem ) ,
101
+ {
102
+ for_each_mut ( & mut func, & mut self . sections ) ;
103
+ }
104
+ }
105
+
106
+ pub fn for_each_mut < ' a , F , I > ( func : & mut F , items : I )
107
+ where
108
+ F : FnMut ( & mut BookItem ) ,
109
+ I : IntoIterator < Item = & ' a mut BookItem > ,
110
+ {
111
+ for item in items {
112
+ if let & mut BookItem :: Chapter ( ref mut ch) = item {
113
+ for_each_mut ( func, & mut ch. sub_items ) ;
114
+ }
115
+
116
+ func ( item) ;
117
+ }
85
118
}
86
119
87
120
/// Enum representing any type of item which can be added to a book.
@@ -224,7 +257,6 @@ impl Display for Chapter {
224
257
}
225
258
}
226
259
227
-
228
260
#[ cfg( test) ]
229
261
mod tests {
230
262
use super :: * ;
@@ -266,7 +298,6 @@ And here is some \
266
298
. write_all ( "Hello World!" . as_bytes ( ) )
267
299
. unwrap ( ) ;
268
300
269
-
270
301
let mut second = Link :: new ( "Nested Chapter 1" , & second_path) ;
271
302
second. number = Some ( SectionNumber ( vec ! [ 1 , 2 ] ) ) ;
272
303
@@ -391,7 +422,6 @@ And here is some \
391
422
] ,
392
423
} ;
393
424
394
-
395
425
let got: Vec < _ > = book. iter ( ) . collect ( ) ;
396
426
397
427
assert_eq ! ( got. len( ) , 5 ) ;
@@ -411,4 +441,39 @@ And here is some \
411
441
412
442
assert_eq ! ( chapter_names, should_be) ;
413
443
}
444
+
445
+ #[ test]
446
+ fn for_each_mut_visits_all_items ( ) {
447
+ let mut book = Book {
448
+ sections : vec ! [
449
+ BookItem :: Chapter ( Chapter {
450
+ name: String :: from( "Chapter 1" ) ,
451
+ content: String :: from( DUMMY_SRC ) ,
452
+ number: None ,
453
+ path: PathBuf :: from( "Chapter_1/index.md" ) ,
454
+ sub_items: vec![
455
+ BookItem :: Chapter ( Chapter :: new(
456
+ "Hello World" ,
457
+ String :: new( ) ,
458
+ "Chapter_1/hello.md" ,
459
+ ) ) ,
460
+ BookItem :: Separator ,
461
+ BookItem :: Chapter ( Chapter :: new(
462
+ "Goodbye World" ,
463
+ String :: new( ) ,
464
+ "Chapter_1/goodbye.md" ,
465
+ ) ) ,
466
+ ] ,
467
+ } ) ,
468
+ BookItem :: Separator ,
469
+ ] ,
470
+ } ;
471
+
472
+ let num_items = book. iter ( ) . count ( ) ;
473
+ let mut visited = 0 ;
474
+
475
+ book. for_each_mut ( |_| visited += 1 ) ;
476
+
477
+ assert_eq ! ( visited, num_items) ;
478
+ }
414
479
}
0 commit comments