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