@@ -408,9 +408,18 @@ impl<const CAP: usize, Item, Gap> LinkedChunk<CAP, Item, Gap> {
408
408
409
409
/// Remove item at a specified position in the [`LinkedChunk`].
410
410
///
411
- /// Because the `position` can be invalid, this method returns a
412
- /// `Result`.
413
- pub fn remove_item_at ( & mut self , position : Position ) -> Result < Item , Error > {
411
+ /// `position` must point to a valid item, otherwise the method returns
412
+ /// `Err`.
413
+ ///
414
+ /// The chunk containing the item represented by `position` may be empty
415
+ /// once the item has been removed. In this case, the chunk can be removed
416
+ /// if `empty_chunk` contains [`EmptyChunk::Remove`], otherwise the chunk is
417
+ /// kept if `empty_chunk` contains [`EmptyChunk::Keep`].
418
+ pub fn remove_item_at (
419
+ & mut self ,
420
+ position : Position ,
421
+ empty_chunk : EmptyChunk ,
422
+ ) -> Result < Item , Error > {
414
423
let chunk_identifier = position. chunk_identifier ( ) ;
415
424
let item_index = position. index ( ) ;
416
425
@@ -446,9 +455,9 @@ impl<const CAP: usize, Item, Gap> LinkedChunk<CAP, Item, Gap> {
446
455
}
447
456
} ;
448
457
449
- // If the ` chunk` can be unlinked , and if the `chunk` is not the first one, we
450
- // can remove it.
451
- if can_unlink_chunk && chunk. is_first_chunk ( ) . not ( ) {
458
+ // If removing empty chunk is desired , and if the `chunk` can be unlinked, and
459
+ // if the `chunk` is not the first one, we can remove it.
460
+ if empty_chunk . remove ( ) && can_unlink_chunk && chunk. is_first_chunk ( ) . not ( ) {
452
461
// Unlink `chunk`.
453
462
chunk. unlink ( & mut self . updates ) ;
454
463
@@ -1336,15 +1345,30 @@ where
1336
1345
}
1337
1346
}
1338
1347
1348
+ /// A type representing what to do when the system has to handle an empty chunk.
1349
+ pub ( crate ) enum EmptyChunk {
1350
+ /// Keep the empty chunk.
1351
+ Keep ,
1352
+
1353
+ /// Remove the empty chunk.
1354
+ Remove ,
1355
+ }
1356
+
1357
+ impl EmptyChunk {
1358
+ fn remove ( & self ) -> bool {
1359
+ matches ! ( self , Self :: Remove )
1360
+ }
1361
+ }
1362
+
1339
1363
#[ cfg( test) ]
1340
1364
mod tests {
1341
1365
use std:: ops:: Not ;
1342
1366
1343
1367
use assert_matches:: assert_matches;
1344
1368
1345
1369
use super :: {
1346
- Chunk , ChunkContent , ChunkIdentifier , ChunkIdentifierGenerator , Error , LinkedChunk ,
1347
- Position ,
1370
+ Chunk , ChunkContent , ChunkIdentifier , ChunkIdentifierGenerator , EmptyChunk , Error ,
1371
+ LinkedChunk , Position ,
1348
1372
} ;
1349
1373
1350
1374
#[ test]
@@ -1950,21 +1974,21 @@ mod tests {
1950
1974
// that. The chunk is removed.
1951
1975
{
1952
1976
let position_of_f = linked_chunk. item_position ( |item| * item == 'f' ) . unwrap ( ) ;
1953
- let removed_item = linked_chunk. remove_item_at ( position_of_f) ?;
1977
+ let removed_item = linked_chunk. remove_item_at ( position_of_f, EmptyChunk :: Remove ) ?;
1954
1978
1955
1979
assert_eq ! ( removed_item, 'f' ) ;
1956
1980
assert_items_eq ! ( linked_chunk, [ 'a' , 'b' , 'c' ] [ 'd' , 'e' ] [ 'g' , 'h' , 'i' ] [ 'j' , 'k' ] ) ;
1957
1981
assert_eq ! ( linked_chunk. len( ) , 10 ) ;
1958
1982
1959
1983
let position_of_e = linked_chunk. item_position ( |item| * item == 'e' ) . unwrap ( ) ;
1960
- let removed_item = linked_chunk. remove_item_at ( position_of_e) ?;
1984
+ let removed_item = linked_chunk. remove_item_at ( position_of_e, EmptyChunk :: Remove ) ?;
1961
1985
1962
1986
assert_eq ! ( removed_item, 'e' ) ;
1963
1987
assert_items_eq ! ( linked_chunk, [ 'a' , 'b' , 'c' ] [ 'd' ] [ 'g' , 'h' , 'i' ] [ 'j' , 'k' ] ) ;
1964
1988
assert_eq ! ( linked_chunk. len( ) , 9 ) ;
1965
1989
1966
1990
let position_of_d = linked_chunk. item_position ( |item| * item == 'd' ) . unwrap ( ) ;
1967
- let removed_item = linked_chunk. remove_item_at ( position_of_d) ?;
1991
+ let removed_item = linked_chunk. remove_item_at ( position_of_d, EmptyChunk :: Remove ) ?;
1968
1992
1969
1993
assert_eq ! ( removed_item, 'd' ) ;
1970
1994
assert_items_eq ! ( linked_chunk, [ 'a' , 'b' , 'c' ] [ 'g' , 'h' , 'i' ] [ 'j' , 'k' ] ) ;
@@ -1985,19 +2009,19 @@ mod tests {
1985
2009
// that. The chunk is NOT removed because it's the first chunk.
1986
2010
{
1987
2011
let first_position = linked_chunk. item_position ( |item| * item == 'a' ) . unwrap ( ) ;
1988
- let removed_item = linked_chunk. remove_item_at ( first_position) ?;
2012
+ let removed_item = linked_chunk. remove_item_at ( first_position, EmptyChunk :: Remove ) ?;
1989
2013
1990
2014
assert_eq ! ( removed_item, 'a' ) ;
1991
2015
assert_items_eq ! ( linked_chunk, [ 'b' , 'c' ] [ 'g' , 'h' , 'i' ] [ 'j' , 'k' ] ) ;
1992
2016
assert_eq ! ( linked_chunk. len( ) , 7 ) ;
1993
2017
1994
- let removed_item = linked_chunk. remove_item_at ( first_position) ?;
2018
+ let removed_item = linked_chunk. remove_item_at ( first_position, EmptyChunk :: Remove ) ?;
1995
2019
1996
2020
assert_eq ! ( removed_item, 'b' ) ;
1997
2021
assert_items_eq ! ( linked_chunk, [ 'c' ] [ 'g' , 'h' , 'i' ] [ 'j' , 'k' ] ) ;
1998
2022
assert_eq ! ( linked_chunk. len( ) , 6 ) ;
1999
2023
2000
- let removed_item = linked_chunk. remove_item_at ( first_position) ?;
2024
+ let removed_item = linked_chunk. remove_item_at ( first_position, EmptyChunk :: Remove ) ?;
2001
2025
2002
2026
assert_eq ! ( removed_item, 'c' ) ;
2003
2027
assert_items_eq ! ( linked_chunk, [ ] [ 'g' , 'h' , 'i' ] [ 'j' , 'k' ] ) ;
@@ -2017,19 +2041,19 @@ mod tests {
2017
2041
// that. The chunk is removed.
2018
2042
{
2019
2043
let first_position = linked_chunk. item_position ( |item| * item == 'g' ) . unwrap ( ) ;
2020
- let removed_item = linked_chunk. remove_item_at ( first_position) ?;
2044
+ let removed_item = linked_chunk. remove_item_at ( first_position, EmptyChunk :: Remove ) ?;
2021
2045
2022
2046
assert_eq ! ( removed_item, 'g' ) ;
2023
2047
assert_items_eq ! ( linked_chunk, [ ] [ 'h' , 'i' ] [ 'j' , 'k' ] ) ;
2024
2048
assert_eq ! ( linked_chunk. len( ) , 4 ) ;
2025
2049
2026
- let removed_item = linked_chunk. remove_item_at ( first_position) ?;
2050
+ let removed_item = linked_chunk. remove_item_at ( first_position, EmptyChunk :: Remove ) ?;
2027
2051
2028
2052
assert_eq ! ( removed_item, 'h' ) ;
2029
2053
assert_items_eq ! ( linked_chunk, [ ] [ 'i' ] [ 'j' , 'k' ] ) ;
2030
2054
assert_eq ! ( linked_chunk. len( ) , 3 ) ;
2031
2055
2032
- let removed_item = linked_chunk. remove_item_at ( first_position) ?;
2056
+ let removed_item = linked_chunk. remove_item_at ( first_position, EmptyChunk :: Remove ) ?;
2033
2057
2034
2058
assert_eq ! ( removed_item, 'i' ) ;
2035
2059
assert_items_eq ! ( linked_chunk, [ ] [ 'j' , 'k' ] ) ;
@@ -2050,15 +2074,15 @@ mod tests {
2050
2074
// The chunk is removed.
2051
2075
{
2052
2076
let position_of_k = linked_chunk. item_position ( |item| * item == 'k' ) . unwrap ( ) ;
2053
- let removed_item = linked_chunk. remove_item_at ( position_of_k) ?;
2077
+ let removed_item = linked_chunk. remove_item_at ( position_of_k, EmptyChunk :: Remove ) ?;
2054
2078
2055
2079
assert_eq ! ( removed_item, 'k' ) ;
2056
2080
#[ rustfmt:: skip]
2057
2081
assert_items_eq ! ( linked_chunk, [ ] [ 'j' ] ) ;
2058
2082
assert_eq ! ( linked_chunk. len( ) , 1 ) ;
2059
2083
2060
2084
let position_of_j = linked_chunk. item_position ( |item| * item == 'j' ) . unwrap ( ) ;
2061
- let removed_item = linked_chunk. remove_item_at ( position_of_j) ?;
2085
+ let removed_item = linked_chunk. remove_item_at ( position_of_j, EmptyChunk :: Remove ) ?;
2062
2086
2063
2087
assert_eq ! ( removed_item, 'j' ) ;
2064
2088
assert_items_eq ! ( linked_chunk, [ ] ) ;
@@ -2092,27 +2116,27 @@ mod tests {
2092
2116
let _ = linked_chunk. updates ( ) . unwrap ( ) . take ( ) ;
2093
2117
2094
2118
let position_of_c = linked_chunk. item_position ( |item| * item == 'c' ) . unwrap ( ) ;
2095
- let removed_item = linked_chunk. remove_item_at ( position_of_c) ?;
2119
+ let removed_item = linked_chunk. remove_item_at ( position_of_c, EmptyChunk :: Remove ) ?;
2096
2120
2097
2121
assert_eq ! ( removed_item, 'c' ) ;
2098
2122
assert_items_eq ! ( linked_chunk, [ 'a' , 'b' ] [ -] [ 'd' ] ) ;
2099
2123
assert_eq ! ( linked_chunk. len( ) , 3 ) ;
2100
2124
2101
2125
let position_of_d = linked_chunk. item_position ( |item| * item == 'd' ) . unwrap ( ) ;
2102
- let removed_item = linked_chunk. remove_item_at ( position_of_d) ?;
2126
+ let removed_item = linked_chunk. remove_item_at ( position_of_d, EmptyChunk :: Remove ) ?;
2103
2127
2104
2128
assert_eq ! ( removed_item, 'd' ) ;
2105
2129
assert_items_eq ! ( linked_chunk, [ 'a' , 'b' ] [ -] ) ;
2106
2130
assert_eq ! ( linked_chunk. len( ) , 2 ) ;
2107
2131
2108
2132
let first_position = linked_chunk. item_position ( |item| * item == 'a' ) . unwrap ( ) ;
2109
- let removed_item = linked_chunk. remove_item_at ( first_position) ?;
2133
+ let removed_item = linked_chunk. remove_item_at ( first_position, EmptyChunk :: Remove ) ?;
2110
2134
2111
2135
assert_eq ! ( removed_item, 'a' ) ;
2112
2136
assert_items_eq ! ( linked_chunk, [ 'b' ] [ -] ) ;
2113
2137
assert_eq ! ( linked_chunk. len( ) , 1 ) ;
2114
2138
2115
- let removed_item = linked_chunk. remove_item_at ( first_position) ?;
2139
+ let removed_item = linked_chunk. remove_item_at ( first_position, EmptyChunk :: Remove ) ?;
2116
2140
2117
2141
assert_eq ! ( removed_item, 'b' ) ;
2118
2142
assert_items_eq ! ( linked_chunk, [ ] [ -] ) ;
@@ -2134,6 +2158,110 @@ mod tests {
2134
2158
Ok ( ( ) )
2135
2159
}
2136
2160
2161
+ #[ test]
2162
+ fn test_remove_item_at_and_keep_empty_chunks ( ) -> Result < ( ) , Error > {
2163
+ use super :: Update :: * ;
2164
+
2165
+ let mut linked_chunk = LinkedChunk :: < 3 , char , ( ) > :: new_with_update_history ( ) ;
2166
+ linked_chunk. push_items_back ( [ 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' ] ) ;
2167
+ assert_items_eq ! ( linked_chunk, [ 'a' , 'b' , 'c' ] [ 'd' , 'e' , 'f' ] [ 'g' , 'h' ] ) ;
2168
+ assert_eq ! ( linked_chunk. len( ) , 8 ) ;
2169
+
2170
+ // Ignore previous updates.
2171
+ let _ = linked_chunk. updates ( ) . unwrap ( ) . take ( ) ;
2172
+
2173
+ // Remove all items from the same chunk. The chunk is empty after that. The
2174
+ // chunk is NOT removed because we asked to keep it.
2175
+ {
2176
+ let position = linked_chunk. item_position ( |item| * item == 'd' ) . unwrap ( ) ;
2177
+ let removed_item = linked_chunk. remove_item_at ( position, EmptyChunk :: Keep ) ?;
2178
+
2179
+ assert_eq ! ( removed_item, 'd' ) ;
2180
+ assert_items_eq ! ( linked_chunk, [ 'a' , 'b' , 'c' ] [ 'e' , 'f' ] [ 'g' , 'h' ] ) ;
2181
+ assert_eq ! ( linked_chunk. len( ) , 7 ) ;
2182
+
2183
+ let removed_item = linked_chunk. remove_item_at ( position, EmptyChunk :: Keep ) ?;
2184
+
2185
+ assert_eq ! ( removed_item, 'e' ) ;
2186
+ assert_items_eq ! ( linked_chunk, [ 'a' , 'b' , 'c' ] [ 'f' ] [ 'g' , 'h' ] ) ;
2187
+ assert_eq ! ( linked_chunk. len( ) , 6 ) ;
2188
+
2189
+ let removed_item = linked_chunk. remove_item_at ( position, EmptyChunk :: Keep ) ?;
2190
+
2191
+ assert_eq ! ( removed_item, 'f' ) ;
2192
+ assert_items_eq ! ( linked_chunk, [ 'a' , 'b' , 'c' ] [ ] [ 'g' , 'h' ] ) ;
2193
+ assert_eq ! ( linked_chunk. len( ) , 5 ) ;
2194
+
2195
+ assert_eq ! (
2196
+ linked_chunk. updates( ) . unwrap( ) . take( ) ,
2197
+ & [
2198
+ RemoveItem { at: Position ( ChunkIdentifier ( 1 ) , 0 ) } ,
2199
+ RemoveItem { at: Position ( ChunkIdentifier ( 1 ) , 0 ) } ,
2200
+ RemoveItem { at: Position ( ChunkIdentifier ( 1 ) , 0 ) } ,
2201
+ ]
2202
+ ) ;
2203
+ }
2204
+
2205
+ // Remove all items from the same chunk. The chunk is empty after that. The
2206
+ // chunk is NOT removed because we asked to keep it.
2207
+ {
2208
+ let position = linked_chunk. item_position ( |item| * item == 'g' ) . unwrap ( ) ;
2209
+ let removed_item = linked_chunk. remove_item_at ( position, EmptyChunk :: Keep ) ?;
2210
+
2211
+ assert_eq ! ( removed_item, 'g' ) ;
2212
+ assert_items_eq ! ( linked_chunk, [ 'a' , 'b' , 'c' ] [ ] [ 'h' ] ) ;
2213
+ assert_eq ! ( linked_chunk. len( ) , 4 ) ;
2214
+
2215
+ let removed_item = linked_chunk. remove_item_at ( position, EmptyChunk :: Keep ) ?;
2216
+
2217
+ assert_eq ! ( removed_item, 'h' ) ;
2218
+ assert_items_eq ! ( linked_chunk, [ 'a' , 'b' , 'c' ] [ ] [ ] ) ;
2219
+ assert_eq ! ( linked_chunk. len( ) , 3 ) ;
2220
+
2221
+ assert_eq ! (
2222
+ linked_chunk. updates( ) . unwrap( ) . take( ) ,
2223
+ & [
2224
+ RemoveItem { at: Position ( ChunkIdentifier ( 2 ) , 0 ) } ,
2225
+ RemoveItem { at: Position ( ChunkIdentifier ( 2 ) , 0 ) } ,
2226
+ ]
2227
+ ) ;
2228
+ }
2229
+
2230
+ // Remove all items from the same chunk. The chunk is empty after that. The
2231
+ // chunk is NOT removed because we asked to keep it.
2232
+ {
2233
+ let position = linked_chunk. item_position ( |item| * item == 'a' ) . unwrap ( ) ;
2234
+ let removed_item = linked_chunk. remove_item_at ( position, EmptyChunk :: Keep ) ?;
2235
+
2236
+ assert_eq ! ( removed_item, 'a' ) ;
2237
+ assert_items_eq ! ( linked_chunk, [ 'b' , 'c' ] [ ] [ ] ) ;
2238
+ assert_eq ! ( linked_chunk. len( ) , 2 ) ;
2239
+
2240
+ let removed_item = linked_chunk. remove_item_at ( position, EmptyChunk :: Keep ) ?;
2241
+
2242
+ assert_eq ! ( removed_item, 'b' ) ;
2243
+ assert_items_eq ! ( linked_chunk, [ 'c' ] [ ] [ ] ) ;
2244
+ assert_eq ! ( linked_chunk. len( ) , 1 ) ;
2245
+
2246
+ let removed_item = linked_chunk. remove_item_at ( position, EmptyChunk :: Keep ) ?;
2247
+
2248
+ assert_eq ! ( removed_item, 'c' ) ;
2249
+ assert_items_eq ! ( linked_chunk, [ ] [ ] [ ] ) ;
2250
+ assert_eq ! ( linked_chunk. len( ) , 0 ) ;
2251
+
2252
+ assert_eq ! (
2253
+ linked_chunk. updates( ) . unwrap( ) . take( ) ,
2254
+ & [
2255
+ RemoveItem { at: Position ( ChunkIdentifier ( 0 ) , 0 ) } ,
2256
+ RemoveItem { at: Position ( ChunkIdentifier ( 0 ) , 0 ) } ,
2257
+ RemoveItem { at: Position ( ChunkIdentifier ( 0 ) , 0 ) } ,
2258
+ ]
2259
+ ) ;
2260
+ }
2261
+
2262
+ Ok ( ( ) )
2263
+ }
2264
+
2137
2265
#[ test]
2138
2266
fn test_insert_gap_at ( ) -> Result < ( ) , Error > {
2139
2267
use super :: Update :: * ;
0 commit comments