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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
//! # Rot
//! Strongly-typed ascii character rotations in the style of Caesar/Rot/Vigenère ciphers.
//! This is only meant to be some fun with type-level integers in Rust.
//! *Please do not use this as an encryption scheme* as it is *laughably* weak.

use typenum::{Integer};
use typenum::consts::{Z0, P26};
use typenum::type_operators::{Same};
use typenum::operator_aliases::{Sum};
use std::marker::PhantomData;
use std::ops::{Add, Rem};

use std::fmt;

/// A wrapper over Ascii `u8` which rotates alphabetic characters by `N`.
/// This is essentially a per-character Caesar cipher with a shift of `N`.
/// A collection of this type may also be used to implement a strongly-typed Vigenère cipher.
/// An interesting characteristic is that this type can only be converted back to
/// a raw `u8` when it has been rotated back into place (i.e `N % 26 == 0`),
/// otherwise it is a compile-time type error to do so.
/// It is also type-parameterized over the rotation length to prevent
/// partial/improper ciphers at compile-time.
/// This makes it impossible to use a byte rotated right by 12 when a type contract
/// requires a byte rotated left by 13, for example.
///
/// # Examples
/// ## Rotate some `u8` bytes by 7 positions to the right.
/// ```
/// use rot::{Rotate, RotU8};
/// use typenum::consts::P7;
/// let s = "a b c";
/// let r : Vec<RotU8<P7>> = s.bytes().rotate_by::<P7>().collect();
/// ```
///
/// ## Try to incorrectly read a rotated byte
/// ```compile_fail
/// use rot::{Rotate, RotU8};
/// use typenum::consts::P1;
/// let c = 'a' as u8;
/// let rotated: RotU8<P1> = c.into();
/// let contents: u8 = rotated.into();  // This will fail to type check!
/// ```
///
/// ## Perform no-op rotations
/// ```
/// use rot::{Rotate, RotU8};
/// use typenum::consts::{Z0, P26};
/// let c = 'a' as u8;
/// let rotated_by_zero: RotU8<Z0> = c.into();
/// let rotated_by_26: RotU8<P26> = c.into();
/// let contents_0: u8 = rotated_by_zero.into();  // This works, `c` wasn't rotated.
/// assert_eq!(contents_0, 'a' as u8);
/// // This works, `c` was rotated back to the start point.
/// let contents_26: u8 = rotated_by_26.into();
/// assert_eq!(contents_26, 'a' as u8);
/// ```
///
/// ## Type safe signatures
/// ```
/// use typenum::{Integer};
/// use typenum::consts::{Z0, P1, N1};
/// use rot::RotU8;
///
/// // This function cannot secretly rotate the byte (unless it rotates it back!)
/// // This would be useful in a trait definition to enforce a stronger contract.
/// fn should_not_rotate<N: Integer>(rc: RotU8<N>) -> RotU8<N> {
///     unimplemented!();
/// }
/// ```
/// 
/// ```compile_fail
/// use typenum::{Integer};
/// use typenum::consts::{Z0, P1, N1};
/// use rot::RotU8;
///
/// fn evil_function<N: Integer>(rc: RotU8<N>) -> RotU8<N> {
///     rc.rotate_by::<P1>() // Compile-time error!
///     // ^ this has type RotU8<N+1>
/// }
/// ```
/// 
/// ```
/// use typenum::{Integer};
/// use typenum::consts::{Z0, P1, N1};
/// use rot::RotU8;
///
/// fn not_so_evil_function<N: Integer>(rc: RotU8<N>) -> RotU8<N> {
///     rc.rotate_by::<Z0>() // Works fine! Adding zero (or a multiple of 26) doesn't rotate.
/// }
/// ````
#[derive(Clone, Copy)]
pub struct RotU8<N: Integer>(u8, PhantomData<N>);

impl<N: Integer> fmt::Display for RotU8<N> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "RotU8<{}>({})", N::to_i8(), self.0 as char)
    }
}

impl<N: Integer> fmt::Debug for RotU8<N> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "RotU8<{}>({})", N::to_i8(), &self.0)
    }
}

impl<N> RotU8<N>
where N: Integer {
    /// Create a new `RotU8<N>` from a pre-rotated character byte.
    fn new(rotated: u8) -> Self {
        Self(rotated, PhantomData)
    }

    /// Get the raw underlying `u8` in rotated form.
    /// ```
    /// use typenum::consts::P1;
    /// use rot::RotU8;
    /// 
    /// let rotated : RotU8<P1> = ('a' as u8).into();
    /// assert_eq!(rotated.as_raw(), 'a' as u8 + 1);
    /// ```
    pub fn as_raw(&self) -> u8 {
        self.0
    }

    /// Wrap an offset character byte (in the positive _or_ negative directions)
    /// and ensure it is in the range `[0, 26)`
    fn wrapping_rotate_mod26(c: i8) -> u8 {
        ((((c + N::to_i8()) % 26) + 26) % 26) as u8
    }

    /// Rotate a character relative to a start point for a 26 character range.
    /// If rotating a lowercase ascii letter, the range starts at `'a'`,
    /// otherwise it starts at `'A'`.
    fn rotate_relative(c: u8, zero_point: u8) -> u8 {
        let offset = c - zero_point;
        Self::wrapping_rotate_mod26(offset as i8) + zero_point
    }

    /// Rotate the raw ascii character `ch` `N` positions,
    /// _if is an alphabetic character_,
    /// otherwise do not change it.
    /// If `N` is positive, this performs a wrapped shift to the right.
    /// If `N` is negative, this performs a wrapped shift to the left.
    /// A zero value for `N` is identity.
    ///
    /// ```
    /// use typenum::consts::{N2, P1, Z0};
    /// use rot::RotU8;
    /// 
    /// assert_eq!(Into::<RotU8<P1>>::into('a' as u8).as_raw(), 'b' as u8);
    /// assert_eq!(Into::<RotU8<N2>>::into('a' as u8).as_raw(), 'y' as u8);
    /// assert_eq!(Into::<RotU8<Z0>>::into('a' as u8).as_raw(), 'a' as u8);
    /// assert_eq!(Into::<RotU8<N2>>::into('7' as u8).as_raw(), '7' as u8);
    /// ```
    fn rotate(ch: u8) -> u8 {
        let c = ch as i8;
        match ch as char {
            'A' ... 'Z' => {
                Self::rotate_relative(c as u8, 'A' as u8)
            }
            'a' ... 'z' => {
                Self::rotate_relative(c as u8, 'a' as u8)
            },
            _ => ch
        }
    }

    /// Chain a rotation by `N` with a rotation by `M`.
    /// ```
    /// use typenum::consts::{P1, P2, P3, N1, N4};
    /// use rot::RotU8;
    ///
    /// let rotate_by_one: RotU8<P1> = ('a' as u8).into();
    /// let rotate_by_three: RotU8<P3> = rotate_by_one.rotate_by::<P2>();
    /// assert_eq!(rotate_by_three.as_raw(), 'd' as u8);
    /// let rotate_back: RotU8<N1> = rotate_by_three.rotate_by::<N4>();
    /// assert_eq!(rotate_back.as_raw(), 'z' as u8);
    /// ```
    pub fn rotate_by<M>(self) -> RotU8<Sum<M, N>>
    where
        M: Add<N> + Integer,
        <M as Add<N>>::Output: Integer
    {
        let c = self.0;
        let rotated = RotU8::<M>::rotate(c);
        RotU8::new(rotated)
    }
}

impl<N> From<u8> for RotU8<N>
where
    N: Integer
{
    fn from(source: u8) -> Self {
        let rotated = Self::rotate(source);
        Self::new(rotated)
    }
}

/// Wraps a raw character/byte iterator and rotates each item.
pub struct RotIter<N: Integer, I: Iterator> {
    iter: I,
    _p: PhantomData<N>
}

/// Rotate `Self::Item` by `N`.
pub trait Rotate
where
    Self: Iterator + Sized
{
    fn rotate_by<N: Integer>(self) -> RotIter<N, Self>;
}

impl<I> Rotate for I
where
    I: Iterator<Item = u8>
{
    fn rotate_by<N: Integer>(self) -> RotIter<N, Self> {
        RotIter {
            iter: self,
            _p: PhantomData
        }
    }
}

impl<N: Integer, I: Iterator<Item = u8>> Iterator for RotIter<N, I> {
    type Item = RotU8<N>;

    fn next(&mut self) -> Option<Self::Item> {
        self.iter
            .next()
            .map(From::from)
    }
}

impl<'a, N> Into<u8> for RotU8<N>
where
    N: Rem<P26> + Integer,
    <N as Rem<P26>>::Output: Integer + Same<Z0>,
{
    fn into(self) -> u8 {
        self.0
    }
}