qrcode_rs/
types.rs

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