Skip to main content

qrcode_rust_shared/
qr_code_model.rs

1//! QR Code Model - Shared constants and types
2
3/// QR Code 模式
4pub struct QRMode;
5
6impl QRMode {
7    pub const MODE_NUMBER: i32 = 1;
8    pub const MODE_ALPHA_NUM: i32 = 2;
9    pub const MODE_8BIT_BYTE: i32 = 4;
10    pub const MODE_KANJI: i32 = 8;
11}
12
13/// 错误纠正级别
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
15pub enum QRErrorCorrectLevel {
16    L = 1, // 低 (~7%)
17    M = 0, // 中 (~15%)
18    Q = 3, // 高 (~25%)
19    #[default]
20    H = 2, // 最高 (~30%)
21}
22
23/// 位置调整图案位置表 (完整 40 版本)
24pub const PATTERN_POSITION_TABLE: &[[i32; 7]] = &[
25    [0, 0, 0, 0, 0, 0, 0],
26    [6, 18, 0, 0, 0, 0, 0],
27    [6, 22, 0, 0, 0, 0, 0],
28    [6, 26, 0, 0, 0, 0, 0],
29    [6, 30, 0, 0, 0, 0, 0],
30    [6, 34, 0, 0, 0, 0, 0],
31    [6, 22, 38, 0, 0, 0, 0],
32    [6, 24, 42, 0, 0, 0, 0],
33    [6, 26, 46, 0, 0, 0, 0],
34    [6, 28, 50, 0, 0, 0, 0],
35    [6, 30, 54, 0, 0, 0, 0],
36    [6, 32, 58, 0, 0, 0, 0],
37    [6, 34, 62, 0, 0, 0, 0],
38    [6, 26, 46, 66, 0, 0, 0],
39    [6, 26, 48, 70, 0, 0, 0],
40    [6, 26, 50, 74, 0, 0, 0],
41    [6, 30, 54, 78, 0, 0, 0],
42    [6, 30, 56, 82, 0, 0, 0],
43    [6, 30, 58, 86, 0, 0, 0],
44    [6, 34, 62, 90, 0, 0, 0],
45    [6, 28, 50, 72, 94, 0, 0],
46    [6, 26, 50, 74, 98, 0, 0],
47    [6, 30, 54, 78, 102, 0, 0],
48    [6, 28, 54, 80, 106, 0, 0],
49    [6, 32, 58, 84, 110, 0, 0],
50    [6, 30, 58, 86, 114, 0, 0],
51    [6, 34, 62, 90, 118, 0, 0],
52    [6, 26, 50, 74, 98, 122, 0],
53    [6, 30, 54, 78, 102, 126, 0],
54    [6, 26, 52, 78, 104, 130, 0],
55    [6, 30, 56, 82, 108, 134, 0],
56    [6, 34, 60, 86, 112, 138, 0],
57    [6, 30, 58, 86, 114, 142, 0],
58    [6, 34, 62, 90, 118, 146, 0],
59    [6, 30, 54, 78, 102, 126, 150],
60    [6, 24, 50, 76, 102, 128, 154],
61    [6, 28, 54, 80, 106, 132, 158],
62    [6, 32, 58, 84, 110, 136, 162],
63    [6, 26, 54, 82, 110, 138, 166],
64    [6, 30, 58, 86, 114, 142, 170],
65];
66
67/// 完整容量表 (40 版本 x 4 纠错级别)
68pub const QR_CODE_LIMIT_LENGTH: &[[i32; 4]] = &[
69    [17, 14, 11, 7],
70    [32, 26, 20, 14],
71    [53, 42, 32, 24],
72    [78, 62, 46, 34],
73    [106, 84, 60, 44],
74    [134, 106, 74, 58],
75    [154, 122, 86, 64],
76    [192, 152, 108, 84],
77    [230, 180, 130, 98],
78    [271, 213, 151, 119],
79    [321, 251, 177, 137],
80    [367, 287, 203, 155],
81    [425, 331, 241, 177],
82    [458, 362, 258, 194],
83    [520, 412, 292, 220],
84    [586, 450, 322, 250],
85    [644, 504, 364, 280],
86    [718, 560, 394, 310],
87    [792, 624, 442, 338],
88    [858, 666, 482, 382],
89    [929, 711, 509, 403],
90    [1003, 779, 565, 439],
91    [1091, 857, 611, 461],
92    [1171, 911, 661, 511],
93    [1273, 997, 715, 535],
94    [1367, 1059, 751, 593],
95    [1465, 1125, 805, 625],
96    [1528, 1190, 868, 658],
97    [1628, 1264, 908, 698],
98    [1732, 1370, 982, 742],
99    [1840, 1452, 1030, 790],
100    [1952, 1538, 1112, 842],
101    [2068, 1628, 1168, 898],
102    [2188, 1722, 1228, 958],
103    [2303, 1809, 1283, 983],
104    [2431, 1911, 1351, 1051],
105    [2563, 1989, 1423, 1093],
106    [2699, 2099, 1499, 1139],
107    [2809, 2213, 1579, 1219],
108    [2953, 2331, 1663, 1273],
109];
110
111/// 获取类型号 (完整版本,支持 40 个版本)
112pub fn get_type_number(text: &str, correct_level: QRErrorCorrectLevel) -> i32 {
113    let length = text.len();
114    let level_map = [1, 0, 3, 2];
115    let level_index = level_map[correct_level as usize];
116    let data_length = length + 2; // 考虑模式指示符和长度字段
117
118    for (i, limits) in QR_CODE_LIMIT_LENGTH.iter().enumerate() {
119        let limit = limits[level_index as usize];
120        if data_length <= limit as usize {
121            return (i + 1) as i32;
122        }
123    }
124    40 // 最大版本号
125}
126
127/// 快速版本:获取最小版本号 (支持 1-10 版本,用于 qrcode-fast)
128pub fn get_min_version(text_len: usize, level: QRErrorCorrectLevel) -> i32 {
129    let level_idx = match level {
130        QRErrorCorrectLevel::L => 0,
131        QRErrorCorrectLevel::M => 1,
132        QRErrorCorrectLevel::Q => 2,
133        QRErrorCorrectLevel::H => 3,
134    };
135
136    for (version, limits) in QR_CODE_LIMIT_LENGTH.iter().enumerate().skip(1).take(10) {
137        if limits[level_idx] >= text_len as i32 {
138            return version as i32;
139        }
140    }
141    10 // 最大支持 Version 10 (fast mode)
142}
143
144/// 获取位置调整图案位置
145pub fn get_pattern_position(type_number: i32) -> Vec<i32> {
146    let pos = &PATTERN_POSITION_TABLE[(type_number - 1) as usize];
147    pos.iter().copied().filter(|&x| x > 0).collect()
148}
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153
154    #[test]
155    fn test_error_correct_level_values() {
156        assert_eq!(QRErrorCorrectLevel::L as i32, 1);
157        assert_eq!(QRErrorCorrectLevel::M as i32, 0);
158        assert_eq!(QRErrorCorrectLevel::Q as i32, 3);
159        assert_eq!(QRErrorCorrectLevel::H as i32, 2);
160    }
161
162    #[test]
163    fn test_get_type_number_basic() {
164        // "Hello" (5 chars) with H level should fit in version 1
165        let version = get_type_number("Hello", QRErrorCorrectLevel::H);
166        assert!((1..=40).contains(&version));
167    }
168
169    #[test]
170    fn test_get_min_version_fast() {
171        // Short text should fit in version 1-2
172        let v = get_min_version(5, QRErrorCorrectLevel::H);
173        assert!((1..=10).contains(&v));
174    }
175
176    #[test]
177    fn test_get_pattern_position() {
178        let pos = get_pattern_position(2);
179        assert_eq!(pos, vec![6, 18]);
180
181        let pos = get_pattern_position(7);
182        assert_eq!(pos, vec![6, 22, 38]);
183    }
184}