1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
//! This crate contains a single function `moveslice`. Its purpose 
//! is to move a chunk within a slice around. It only uses safe functions,
//! and acts efficiently by using the 
//! [`split_at_mut`](https://doc.rust-lang.org/std/primitive.slice.html#method.split_at_mut)
//! and 
//! [`rotate_left`](https://doc.rust-lang.org/std/primitive.slice.html#method.rotate_left)/
//! [`rotate_right`](https://doc.rust-lang.org/std/primitive.slice.html#method.rotate_right)
//! functions.
//! 
//! # Examples:
//! 
//! ```
//! use moveslice::moveslice;
//! 
//! let mut arr = [1,2,3,4,5,6,7,8,9];
//! 
//! // The following moves the slice 3..6 to index 1.
//! // In effect, it moves [4,5,6] over to where [2] is.
//! moveslice(&mut arr, (3,6), 1);
//! assert_eq!(arr, [1,4,5,6,2,3,7,8,9]);
//! 
//! // The following moves the slice 3..6 to index 6.
//! // In effect, it moves [6,2,3] over to where [7] is.
//! moveslice(&mut arr, (3,6), 6);
//! assert_eq!(arr, [1,4,5,7,8,9,6,2,3]);
//! 
//! // The following attempts to move the slice beyond boundaries.
//! // The index given is 7, which exists in the array, but the 
//! // last element of the chunk will not fit (7 + 3 = 10 > 9).
//! // Therefore, the following should fail.
//! # #[should_panic]
//! # fn main() {
//! # let mut arr = [1,2,3,4,5,6,7,8,9];
//! let result = moveslice(&mut arr, (3,6), 7);
//! # }
//! 
//! // You could pass the destination as the same value as chunk.0.
//! // However this would mean nothing is moved.
//! // Since it's not technically an error however, only a warning is logged.
//! moveslice(&mut arr, (0,3), 0);
//! ```

/// Moves a slice around in an array.
/// Works by splitting and rotating.
/// 
/// There are three parameters:
/// 
/// - `slice` : The slice to modify.
/// - `chunk` : A tuple with the boundaries of the chunk you want to move.
/// - `destination` : Where you want to move the chunk.
/// 
/// Note that the destination specifies where the *first* element of the chunk 
/// will be. As a result, its maximum value is not the length of the slice.
/// 
/// For example, if you have a slice with size 10, and you're moving a chunk of 
/// size 3 around, the maximum value for the destination is *10-3=* ***7***.
/// 
/// # Panics
/// Panics when the destination leads the chunk out of bounds.
/// 
/// In the example above, if I specify a destination of 8, the function will panic,
/// showing what would be the placement of the chunk, and the length of the slice.
/// 
/// ```should_panic
/// # use moveslice::moveslice;
/// # fn main() {
/// let mut arr = [1,2,3,4,5,6,7,8,9];
/// let result = moveslice(&mut arr, (3,6), 7); // will panic
/// # }
/// ```
pub fn moveslice<T>(slice: &mut [T], chunk: (usize, usize), destination: usize) {
    if destination > chunk.0 {
        let len = { slice.len() };
        let index1 = chunk.0;
        let index2 = destination;
        let chunksize = chunk.1 - chunk.0;

        let (_, mid) = slice.split_at_mut(index1);

        let mid = if destination + chunksize <= len {
            mid.split_at_mut(index2).0
        } else {
            panic!("Direction goes beyond slice [len = {}, destination = {}..{}]. ",
                    len, destination, destination + chunksize);
        };

        mid.rotate_left(chunk.1-chunk.0);
    } else if destination < chunk.0 {
        let index1 = destination;
        let index2 = chunk.1 - destination;

        let (_, mid) = slice.split_at_mut(index1);

        let mid = mid.split_at_mut(index2).0;

        mid.rotate_right(chunk.1-chunk.0);
    } else {
        println!("The destination is the same as the start of the chunk. Nothing was moved.");
    }
}