qr_code/
types.rs

1//! Type to support QR code encoding
2
3use crate::cast::As;
4use crate::structured::StructuredQrError;
5use std::cmp::{Ordering, PartialOrd};
6use std::default::Default;
7use std::fmt::{Display, Error, Formatter};
8use std::ops::Not;
9
10/// `QrError` encodes the error encountered when generating a QR code.
11#[derive(Debug, PartialEq, Eq, Copy, Clone)]
12pub enum QrError {
13    /// The data is too long to encode into a QR code for the given version.
14    DataTooLong,
15
16    /// The provided version / error correction level combination is invalid.
17    InvalidVersion,
18
19    /// Some characters in the data cannot be supported by the provided QR code
20    /// version.
21    UnsupportedCharacterSet,
22
23    /// The provided ECI designator is invalid. A valid designator should be
24    /// between 0 and 999999.
25    InvalidEciDesignator,
26
27    /// A character not belonging to the character set is found.
28    InvalidCharacter,
29
30    ///
31    Structured(StructuredQrError),
32}
33
34impl Display for QrError {
35    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
36        match *self {
37            QrError::DataTooLong => fmt.write_str("data too long"),
38            QrError::InvalidVersion => fmt.write_str("invalid version"),
39            QrError::UnsupportedCharacterSet => fmt.write_str("unsupported character set"),
40            QrError::InvalidEciDesignator => fmt.write_str("invalid ECI designator"),
41            QrError::InvalidCharacter => fmt.write_str("invalid character"),
42            QrError::Structured(e) => write!(fmt, "{}", e),
43        }
44    }
45}
46
47impl ::std::error::Error for QrError {}
48
49/// `QrResult` is a convenient alias for a QR code generation result.
50pub type QrResult<T> = Result<T, QrError>;
51
52//}}}
53//------------------------------------------------------------------------------
54//{{{ Color
55
56/// The color of a module.
57#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
58pub enum Color {
59    /// The module is light colored.
60    Light,
61    /// The module is dark colored.
62    Dark,
63}
64
65impl Color {
66    /// Selects a value according to color of the module. Equivalent to
67    /// `if self != Color::Light { dark } else { light }`.
68    ///
69    /// # Examples
70    ///
71    /// ```rust
72    /// # use qr_code::types::Color;
73    /// assert_eq!(Color::Light.select(1, 0), 0);
74    /// assert_eq!(Color::Dark.select("black", "white"), "black");
75    /// ```
76    pub fn select<T>(self, dark: T, light: T) -> T {
77        match self {
78            Color::Light => light,
79            Color::Dark => dark,
80        }
81    }
82}
83
84impl Not for Color {
85    type Output = Self;
86    fn not(self) -> Self {
87        match self {
88            Color::Light => Color::Dark,
89            Color::Dark => Color::Light,
90        }
91    }
92}
93
94//}}}
95//------------------------------------------------------------------------------
96//{{{ Error correction level
97
98/// The error correction level. It allows the original information be recovered
99/// even if parts of the code is damaged.
100#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)]
101#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
102pub enum EcLevel {
103    /// Low error correction. Allows up to 7% of wrong blocks.
104    L = 0,
105
106    /// Medium error correction (default). Allows up to 15% of wrong blocks.
107    M = 1,
108
109    /// "Quartile" error correction. Allows up to 25% of wrong blocks.
110    Q = 2,
111
112    /// High error correction. Allows up to 30% of wrong blocks.
113    H = 3,
114}
115
116//}}}
117//------------------------------------------------------------------------------
118//{{{ Version
119
120/// In QR code terminology, `Version` means the size of the generated image.
121/// Larger version means the size of code is larger, and therefore can carry
122/// more information.
123///
124/// The smallest version is `Version::Normal(1)` of size 21×21, and the largest
125/// is `Version::Normal(40)` of size 177×177.
126#[derive(Debug, PartialEq, Eq, Copy, Clone)]
127pub enum Version {
128    /// A normal QR code version. The parameter should be between 1 and 40.
129    Normal(i16),
130
131    /// A Micro QR code version. The parameter should be between 1 and 4.
132    Micro(i16),
133}
134
135impl Version {
136    /// Get the number of "modules" on each size of the QR code, i.e. the width
137    /// and height of the code.
138    pub fn width(self) -> i16 {
139        match self {
140            Version::Normal(v) => v * 4 + 17,
141            Version::Micro(v) => v * 2 + 9,
142        }
143    }
144
145    /// Obtains an object from a hard-coded table.
146    ///
147    /// The table must be a 44×4 array. The outer array represents the content
148    /// for each version. The first 40 entry corresponds to QR code versions 1
149    /// to 40, and the last 4 corresponds to Micro QR code version 1 to 4. The
150    /// inner array represents the content in each error correction level, in
151    /// the order [L, M, Q, H].
152    ///
153    /// # Errors
154    ///
155    /// If the entry compares equal to the default value of `T`, this method
156    /// returns `Err(QrError::InvalidVersion)`.
157    pub fn fetch<T>(self, ec_level: EcLevel, table: &[[T; 4]]) -> QrResult<T>
158    where
159        T: PartialEq + Default + Copy,
160    {
161        match self {
162            Version::Normal(v @ 1..=40) => {
163                return Ok(table[(v - 1).as_usize()][ec_level as usize]);
164            }
165            Version::Micro(v @ 1..=4) => {
166                let obj = table[(v + 39).as_usize()][ec_level as usize];
167                if obj != T::default() {
168                    return Ok(obj);
169                }
170            }
171            _ => {}
172        }
173        Err(QrError::InvalidVersion)
174    }
175
176    /// The number of bits needed to encode the mode indicator.
177    pub fn mode_bits_count(self) -> usize {
178        match self {
179            Version::Micro(a) => (a - 1).as_usize(),
180            _ => 4,
181        }
182    }
183
184    /// Checks whether is version refers to a Micro QR code.
185    pub fn is_micro(self) -> bool {
186        match self {
187            Version::Normal(_) => false,
188            Version::Micro(_) => true,
189        }
190    }
191}
192
193//}}}
194//------------------------------------------------------------------------------
195//{{{ Mode indicator
196
197/// The mode indicator, which specifies the character set of the encoded data.
198#[derive(Debug, PartialEq, Eq, Copy, Clone)]
199pub enum Mode {
200    /// The data contains only characters 0 to 9.
201    Numeric,
202
203    /// The data contains only uppercase letters (A–Z), numbers (0–9) and a few
204    /// punctuations marks (space, `$`, `%`, `*`, `+`, `-`, `.`, `/`, `:`).
205    Alphanumeric,
206
207    /// The data contains arbitrary binary data.
208    Byte,
209
210    /// The data contains Shift-JIS-encoded double-byte text.
211    Kanji,
212}
213
214impl Mode {
215    /// Computes the number of bits needed to encode the data length.
216    ///
217    ///     use qr_code::types::{Version, Mode};
218    ///
219    ///     assert_eq!(Mode::Numeric.length_bits_count(Version::Normal(1)), 10);
220    ///
221    /// This method will return `Err(QrError::UnsupportedCharacterSet)` if the
222    /// mode is not supported in the given version.
223    pub fn length_bits_count(self, version: Version) -> usize {
224        match version {
225            Version::Micro(a) => {
226                let a = a.as_usize();
227                match self {
228                    Mode::Numeric => 2 + a,
229                    Mode::Alphanumeric | Mode::Byte => 1 + a,
230                    Mode::Kanji => a,
231                }
232            }
233            Version::Normal(1..=9) => match self {
234                Mode::Numeric => 10,
235                Mode::Alphanumeric => 9,
236                Mode::Byte | Mode::Kanji => 8,
237            },
238            Version::Normal(10..=26) => match self {
239                Mode::Numeric => 12,
240                Mode::Alphanumeric => 11,
241                Mode::Byte => 16,
242                Mode::Kanji => 10,
243            },
244            Version::Normal(_) => match self {
245                Mode::Numeric => 14,
246                Mode::Alphanumeric => 13,
247                Mode::Byte => 16,
248                Mode::Kanji => 12,
249            },
250        }
251    }
252
253    /// Computes the number of bits needed to some data of a given raw length.
254    ///
255    ///     use qr_code::types::Mode;
256    ///
257    ///     assert_eq!(Mode::Numeric.data_bits_count(7), 24);
258    ///
259    /// Note that in Kanji mode, the `raw_data_len` is the number of Kanjis,
260    /// i.e. half the total size of bytes.
261    pub fn data_bits_count(self, raw_data_len: usize) -> usize {
262        match self {
263            Mode::Numeric => (raw_data_len * 10 + 2) / 3,
264            Mode::Alphanumeric => (raw_data_len * 11 + 1) / 2,
265            Mode::Byte => raw_data_len * 8,
266            Mode::Kanji => raw_data_len * 13,
267        }
268    }
269
270    /// Find the lowest common mode which both modes are compatible with.
271    ///
272    ///     use qr_code::types::Mode;
273    ///
274    ///     let a = Mode::Numeric;
275    ///     let b = Mode::Kanji;
276    ///     let c = a.max(b);
277    ///     assert!(a <= c);
278    ///     assert!(b <= c);
279    ///
280    pub fn max(self, other: Self) -> Self {
281        match self.partial_cmp(&other) {
282            Some(Ordering::Less) | Some(Ordering::Equal) => other,
283            Some(Ordering::Greater) => self,
284            None => Mode::Byte,
285        }
286    }
287
288    pub(crate) const ALL_MODES: &'static [Mode] =
289        &[Mode::Numeric, Mode::Alphanumeric, Mode::Byte, Mode::Kanji];
290}
291
292impl PartialOrd for Mode {
293    /// Defines a partial ordering between modes. If `a <= b`, then `b` contains
294    /// a superset of all characters supported by `a`.
295    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
296        match (*self, *other) {
297            (Mode::Numeric, Mode::Alphanumeric)
298            | (Mode::Numeric, Mode::Byte)
299            | (Mode::Alphanumeric, Mode::Byte)
300            | (Mode::Kanji, Mode::Byte) => Some(Ordering::Less),
301            (Mode::Alphanumeric, Mode::Numeric)
302            | (Mode::Byte, Mode::Numeric)
303            | (Mode::Byte, Mode::Alphanumeric)
304            | (Mode::Byte, Mode::Kanji) => Some(Ordering::Greater),
305            (a, b) if a == b => Some(Ordering::Equal),
306            _ => None,
307        }
308    }
309}
310
311#[cfg(test)]
312mod mode_tests {
313    use crate::types::Mode::{Alphanumeric, Byte, Kanji, Numeric};
314
315    #[test]
316    fn test_mode_order() {
317        assert!(Numeric < Alphanumeric);
318        assert!(Byte > Kanji);
319        assert!(!(Numeric < Kanji));
320        assert!(!(Numeric >= Kanji));
321    }
322
323    #[test]
324    fn test_max() {
325        assert_eq!(Byte.max(Kanji), Byte);
326        assert_eq!(Numeric.max(Alphanumeric), Alphanumeric);
327        assert_eq!(Alphanumeric.max(Alphanumeric), Alphanumeric);
328        assert_eq!(Numeric.max(Kanji), Byte);
329        assert_eq!(Kanji.max(Numeric), Byte);
330        assert_eq!(Alphanumeric.max(Numeric), Alphanumeric);
331        assert_eq!(Kanji.max(Kanji), Kanji);
332    }
333}
334
335//}}}