qrcode/
types.rs

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