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 if let Version::Micro(a) = self {
175 (a - 1).as_usize()
176 } else {
177 4
178 }
179 }
180
181 /// Checks whether is version refers to a Micro QR code.
182 pub fn is_micro(self) -> bool {
183 matches!(self, Version::Micro(_))
184 }
185}
186
187//}}}
188//------------------------------------------------------------------------------
189//{{{ Mode indicator
190
191/// The mode indicator, which specifies the character set of the encoded data.
192#[derive(Debug, PartialEq, Eq, Copy, Clone)]
193pub enum Mode {
194 /// The data contains only characters 0 to 9.
195 Numeric,
196
197 /// The data contains only uppercase letters (A–Z), numbers (0–9) and a few
198 /// punctuations marks (space, `$`, `%`, `*`, `+`, `-`, `.`, `/`, `:`).
199 Alphanumeric,
200
201 /// The data contains arbitrary binary data.
202 Byte,
203
204 /// The data contains Shift-JIS-encoded double-byte text.
205 Kanji,
206}
207
208impl Mode {
209 /// Computes the number of bits needed to encode the data length.
210 ///
211 /// use qrcode::types::{Version, Mode};
212 ///
213 /// assert_eq!(Mode::Numeric.length_bits_count(Version::Normal(1)), 10);
214 ///
215 /// This method will return `Err(QrError::UnsupportedCharacterSet)` if the
216 /// mode is not supported in the given version.
217 pub fn length_bits_count(self, version: Version) -> usize {
218 match version {
219 Version::Micro(a) => {
220 let a = a.as_usize();
221 match self {
222 Mode::Numeric => 2 + a,
223 Mode::Alphanumeric | Mode::Byte => 1 + a,
224 Mode::Kanji => a,
225 }
226 }
227 Version::Normal(1..=9) => match self {
228 Mode::Numeric => 10,
229 Mode::Alphanumeric => 9,
230 Mode::Byte | Mode::Kanji => 8,
231 },
232 Version::Normal(10..=26) => match self {
233 Mode::Numeric => 12,
234 Mode::Alphanumeric => 11,
235 Mode::Byte => 16,
236 Mode::Kanji => 10,
237 },
238 Version::Normal(_) => match self {
239 Mode::Numeric => 14,
240 Mode::Alphanumeric => 13,
241 Mode::Byte => 16,
242 Mode::Kanji => 12,
243 },
244 }
245 }
246
247 /// Computes the number of bits needed to some data of a given raw length.
248 ///
249 /// use qrcode::types::Mode;
250 ///
251 /// assert_eq!(Mode::Numeric.data_bits_count(7), 24);
252 ///
253 /// Note that in Kanji mode, the `raw_data_len` is the number of Kanjis,
254 /// i.e. half the total size of bytes.
255 pub fn data_bits_count(self, raw_data_len: usize) -> usize {
256 match self {
257 Mode::Numeric => (raw_data_len * 10 + 2) / 3,
258 Mode::Alphanumeric => (raw_data_len * 11 + 1) / 2,
259 Mode::Byte => raw_data_len * 8,
260 Mode::Kanji => raw_data_len * 13,
261 }
262 }
263
264 /// Find the lowest common mode which both modes are compatible with.
265 ///
266 /// use qrcode::types::Mode;
267 ///
268 /// let a = Mode::Numeric;
269 /// let b = Mode::Kanji;
270 /// let c = a.max(b);
271 /// assert!(a <= c);
272 /// assert!(b <= c);
273 ///
274 #[must_use]
275 pub fn max(self, other: Self) -> Self {
276 match self.partial_cmp(&other) {
277 Some(Ordering::Greater) => self,
278 Some(_) => other,
279 None => Mode::Byte,
280 }
281 }
282}
283
284impl PartialOrd for Mode {
285 /// Defines a partial ordering between modes. If `a <= b`, then `b` contains
286 /// a superset of all characters supported by `a`.
287 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
288 match (*self, *other) {
289 (a, b) if a == b => Some(Ordering::Equal),
290 (Mode::Numeric, Mode::Alphanumeric) | (_, Mode::Byte) => Some(Ordering::Less),
291 (Mode::Alphanumeric, Mode::Numeric) | (Mode::Byte, _) => Some(Ordering::Greater),
292 _ => None,
293 }
294 }
295}
296
297#[cfg(test)]
298mod mode_tests {
299 use crate::types::Mode::{Alphanumeric, Byte, Kanji, Numeric};
300
301 #[test]
302 fn test_mode_order() {
303 assert!(Numeric < Alphanumeric);
304 assert!(Byte > Kanji);
305 assert!(!(Numeric < Kanji));
306 assert!(!(Numeric >= Kanji));
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//}}}