Skip to main content

qrcode_rust/
lib.rs

1//! @veaba/qrcode-rust - Pure Rust QRCode Generator
2//!
3//! A pure Rust QRCode generator library.
4//! Provides consistent API with qrcode-node and qrcode-bun.
5
6// 本地模块:核心 QRCode 实现(特有,不共享)
7mod qr_code;
8
9// 从 qrcode-rust-shared 重新导出
10pub use qrcode_rust_shared::{
11    qr_8bit_byte::QR8bitByte,
12    qr_bit_buffer::BitBuffer,
13    qr_code_model::{get_type_number, QRErrorCorrectLevel, QRMode, PATTERN_POSITION_TABLE},
14    qr_math::QRMath,
15    qr_polynomial::Polynomial,
16    qr_rs_block::{get_rs_blocks, QRRSBlock},
17    qr_util::{get_bch_digit, get_length_in_bits},
18};
19
20// 重新导出本地模块
21pub use qr_code::{QRCode, QRCodeOptions};
22
23// ============================================
24// QRCode Native 包装器
25// ============================================
26
27pub struct QRCodeNative {
28    qr: QRCode,
29}
30
31impl QRCodeNative {
32    pub fn new(text: &str, correct_level: QRErrorCorrectLevel) -> Self {
33        let mut qr = QRCode::with_options(QRCodeOptions {
34            width: 256,
35            height: 256,
36            color_dark: String::from("#000000"),
37            color_light: String::from("#ffffff"),
38            correct_level,
39        });
40        qr.make_code(text);
41        QRCodeNative { qr }
42    }
43
44    pub fn module_count(&self) -> i32 {
45        self.qr.get_module_count()
46    }
47
48    pub fn get_module_count(&self) -> i32 {
49        self.qr.get_module_count()
50    }
51
52    pub fn is_dark(&self, row: i32, col: i32) -> bool {
53        self.qr.is_dark(row, col)
54    }
55
56    pub fn to_svg(&self, size: i32) -> String {
57        let count = self.qr.get_module_count();
58        if count == 0 {
59            return String::new();
60        }
61
62        let cell_size = size / count;
63        let actual_size = cell_size * count;
64        let offset = (size - actual_size) / 2;
65
66        let mut svg = format!(
67            r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {} {}" width="{}" height="{}">"#,
68            size, size, size, size
69        );
70
71        svg.push_str(&format!(
72            r#"<rect width="{}" height="{}" fill="{}"/>"#,
73            size, size, self.qr.options.color_light
74        ));
75
76        for row in 0..count {
77            for col in 0..count {
78                if self.qr.is_dark(row, col) {
79                    svg.push_str(&format!(
80                        r#"<rect x="{}" y="{}" width="{}" height="{}" fill="{}"/>"#,
81                        col * cell_size + offset,
82                        row * cell_size + offset,
83                        cell_size,
84                        cell_size,
85                        self.qr.options.color_dark
86                    ));
87                }
88            }
89        }
90
91        svg.push_str("</svg>");
92        svg
93    }
94}
95
96impl Default for QRCodeNative {
97    fn default() -> Self {
98        Self::new("", QRErrorCorrectLevel::H)
99    }
100}
101
102// ============================================
103// 集成测试
104// ============================================
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[test]
111    fn test_qrcode_basic_creation() {
112        let mut qr = QRCode::new();
113        qr.make_code("Hello World");
114
115        assert!(qr.module_count > 0, "模块数应该大于 0");
116        assert!(qr.type_number > 0, "类型号应该大于 0");
117
118        // 检查 data_cache
119        if let Some(ref data) = qr.data_cache {
120            println!("\n=== test_qrcode_basic_creation ===");
121            println!("data_cache 长度: {} 字节", data.len());
122            println!("期望: 16 字节数据 + 28 字节纠错 = 44 字节 (版本 2-H)");
123            println!("数据部分 (前 16 字节):");
124            for (i, byte) in data.iter().enumerate().take(16) {
125                println!("  [{:2}] = 0x{:02X}", i, byte);
126            }
127            println!("纠错码部分 (后 28 字节):");
128            for (i, byte) in data.iter().enumerate().skip(16) {
129                println!("  [{:2}] = 0x{:02X}", i, byte);
130            }
131        }
132    }
133
134    #[test]
135    fn test_qrcode_with_options() {
136        let mut qr = QRCode::with_options(QRCodeOptions {
137            width: 256,
138            height: 256,
139            color_dark: String::from("#000000"),
140            color_light: String::from("#ffffff"),
141            correct_level: QRErrorCorrectLevel::H,
142        });
143        qr.make_code("Test");
144
145        assert_eq!(qr.options.color_dark, "#000000");
146        assert_eq!(qr.options.color_light, "#ffffff");
147    }
148
149    #[test]
150    fn test_qrcode_is_dark_in_range() {
151        let mut qr = QRCode::new();
152        qr.make_code("Test");
153
154        let count = qr.module_count;
155
156        // 测试边界内的访问
157        for row in 0..count {
158            for col in 0..count {
159                let _ = qr.is_dark(row, col);
160            }
161        }
162    }
163
164    #[test]
165    #[should_panic]
166    fn test_qrcode_is_dark_out_of_range() {
167        let mut qr = QRCode::new();
168        qr.make_code("Test");
169
170        let count = qr.module_count;
171        // 这应该 panic
172        qr.is_dark(count, 0);
173    }
174
175    #[test]
176    fn test_qrcode_svg_generation() {
177        let mut qr = QRCode::new();
178        qr.make_code("Hello");
179
180        let svg = qr.get_svg();
181
182        assert!(!svg.is_empty(), "SVG 不应该为空");
183        assert!(svg.contains("<svg"), "SVG 应该包含 <svg 标签");
184        assert!(svg.contains("</svg>"), "SVG 应该包含 </svg> 标签");
185    }
186
187    #[test]
188    fn test_qrcode_position_detection_patterns() {
189        // 测试位置探测图案是否正确
190        let mut qr = QRCode::new();
191        qr.make_code("Test");
192
193        let count = qr.module_count;
194
195        // 左上角位置探测图案的角落应该是深色的
196        assert!(qr.is_dark(0, 0), "左上角 (0,0) 应该是深色");
197        assert!(qr.is_dark(0, 6), "左上角 (0,6) 应该是深色");
198        assert!(qr.is_dark(6, 0), "左上角 (6,0) 应该是深色");
199        assert!(qr.is_dark(6, 6), "左上角 (6,6) 应该是深色");
200
201        // 右上角位置探测图案
202        assert!(
203            qr.is_dark(0, count - 1),
204            "右上角 (0,{}) 应该是深色",
205            count - 1
206        );
207        assert!(
208            qr.is_dark(0, count - 7),
209            "右上角 (0,{}) 应该是深色",
210            count - 7
211        );
212        assert!(
213            qr.is_dark(6, count - 1),
214            "右上角 (6,{}) 应该是深色",
215            count - 1
216        );
217        assert!(
218            qr.is_dark(6, count - 7),
219            "右上角 (6,{}) 应该是深色",
220            count - 7
221        );
222
223        // 左下角位置探测图案
224        assert!(
225            qr.is_dark(count - 1, 0),
226            "左下角 ({},0) 应该是深色",
227            count - 1
228        );
229        assert!(
230            qr.is_dark(count - 7, 0),
231            "左下角 ({},0) 应该是深色",
232            count - 7
233        );
234        assert!(
235            qr.is_dark(count - 1, 6),
236            "左下角 ({},6) 应该是深色",
237            count - 1
238        );
239        assert!(
240            qr.is_dark(count - 7, 6),
241            "左下角 ({},6) 应该是深色",
242            count - 7
243        );
244    }
245
246    #[test]
247    fn test_qrcode_timing_patterns() {
248        // 测试定时图案
249        let mut qr = QRCode::new();
250        qr.make_code("Test");
251
252        let count = qr.module_count;
253
254        // 水平定时图案应该在第 6 行,从第 8 列开始
255        for col in 8..count - 8 {
256            let expected = col % 2 == 0;
257            assert_eq!(
258                qr.is_dark(6, col),
259                expected,
260                "水平定时图案在 (6,{}) 不匹配",
261                col
262            );
263        }
264
265        // 垂直定时图案应该在第 6 列,从第 8 行开始
266        for row in 8..count - 8 {
267            let expected = row % 2 == 0;
268            assert_eq!(
269                qr.is_dark(row, 6),
270                expected,
271                "垂直定时图案在 ({},6) 不匹配",
272                row
273            );
274        }
275    }
276
277    #[test]
278    fn test_different_error_correction_levels() {
279        let test_data = "Hello World";
280
281        for level in [
282            QRErrorCorrectLevel::L,
283            QRErrorCorrectLevel::M,
284            QRErrorCorrectLevel::Q,
285            QRErrorCorrectLevel::H,
286        ] {
287            let mut qr = QRCode::with_options(QRCodeOptions {
288                width: 256,
289                height: 256,
290                color_dark: String::from("#000000"),
291                color_light: String::from("#ffffff"),
292                correct_level: level,
293            });
294            qr.make_code(test_data);
295
296            assert!(
297                qr.module_count > 0,
298                "纠错级别 {:?} 应该生成有效的二维码",
299                level
300            );
301        }
302    }
303
304    #[test]
305    fn test_empty_string() {
306        let mut qr = QRCode::new();
307        qr.make_code("");
308
309        assert!(qr.module_count > 0, "空字符串也应该生成二维码");
310    }
311
312    #[test]
313    fn test_long_text() {
314        let long_text = "a".repeat(100);
315        let mut qr = QRCode::new();
316        qr.make_code(&long_text);
317
318        assert!(qr.module_count > 0, "长文本应该生成二维码");
319        assert!(qr.type_number > 1, "长文本应该使用更高的类型号");
320    }
321
322    #[test]
323    fn test_complex_text_hello_world_123() {
324        // 测试复杂文本 "Test QR Code 123"
325        let text = "Test QR Code 123";
326        let mut qr = QRCode::new();
327        qr.make_code(text);
328
329        assert!(qr.module_count > 0, "复杂文本应该生成二维码");
330        assert!(qr.type_number >= 2, "此文本应该使用类型号 >= 2");
331
332        // 打印调试信息
333        println!("\n=== test_complex_text_hello_world_123 (qrcode-rust) ===");
334        println!("文本: {}", text);
335        println!("类型号: {}", qr.type_number);
336        println!("模块数: {}", qr.module_count);
337
338        // 检查 data_cache
339        if let Some(ref data) = qr.data_cache {
340            println!("data_cache 长度: {} 字节", data.len());
341            println!("数据字节 (前 32 字节):");
342            for (i, byte) in data.iter().enumerate().take(32) {
343                print!("{:02X} ", byte);
344                if (i + 1) % 16 == 0 {
345                    println!();
346                }
347            }
348            println!();
349        }
350    }
351
352    #[test]
353    fn test_complex_text_various() {
354        // 测试多种复杂文本
355        let test_cases = vec![
356            "Test QR Code 123",
357            "Hello World! 2024",
358            "https://example.com/path?query=1&foo=bar",
359            "Email: test@example.com | Phone: +1-234-567-8900",
360            "WiFi:T:WPA;S:MyNetwork;P:MyPassword;;",
361        ];
362
363        for text in test_cases {
364            let mut qr = QRCode::new();
365            qr.make_code(text);
366
367            assert!(qr.module_count > 0, "文本 '{}' 应该生成二维码", text);
368            println!(
369                "文本 '{}' -> 类型号 {}, 模块数 {}",
370                text, qr.type_number, qr.module_count
371            );
372        }
373    }
374
375    #[test]
376    fn test_get_rs_blocks() {
377        // 测试类型号 1,所有纠错级别
378        for level in [
379            QRErrorCorrectLevel::L,
380            QRErrorCorrectLevel::M,
381            QRErrorCorrectLevel::Q,
382            QRErrorCorrectLevel::H,
383        ] {
384            let blocks = get_rs_blocks(1, level);
385            assert!(
386                !blocks.is_empty(),
387                "类型号 1 纠错级别 {:?} 应该有 RS 块",
388                level
389            );
390        }
391
392        // 测试类型号 2,所有纠错级别
393        for level in [
394            QRErrorCorrectLevel::L,
395            QRErrorCorrectLevel::M,
396            QRErrorCorrectLevel::Q,
397            QRErrorCorrectLevel::H,
398        ] {
399            let blocks = get_rs_blocks(2, level);
400            assert!(
401                !blocks.is_empty(),
402                "类型号 2 纠错级别 {:?} 应该有 RS 块",
403                level
404            );
405        }
406    }
407}