1use crate::types::{Color, EcLevel, Version};
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub struct ModuleView<'a> {
16 modules: &'a [Color],
17 width: usize,
18 height: usize,
19}
20
21impl<'a> ModuleView<'a> {
22 #[must_use]
26 pub const fn new(modules: &'a [Color], width: usize) -> Option<Self> {
27 Self::new_rect(modules, width, width)
28 }
29
30 #[must_use]
38 pub const fn new_rect(modules: &'a [Color], width: usize, height: usize) -> Option<Self> {
39 if !has_valid_module_geometry(modules.len(), width, height) {
40 return None;
41 }
42 Some(Self { modules, width, height })
43 }
44
45 #[must_use]
50 pub fn row_range(&self, start: usize, end: usize) -> Option<Self> {
51 if start >= end || end > self.height {
52 return None;
53 }
54 let height = end - start;
55 let start = start.checked_mul(self.width)?;
56 let end = end.checked_mul(self.width)?;
57 Some(Self { modules: self.modules.get(start..end)?, width: self.width, height })
58 }
59}
60
61const fn has_valid_module_geometry(len: usize, width: usize, height: usize) -> bool {
62 if width == 0 || height == 0 {
63 return false;
64 }
65 match width.checked_mul(height) {
66 Some(expected) => len == expected,
67 None => false,
68 }
69}
70
71impl ModuleSource for ModuleView<'_> {
72 fn get(&self, x: usize, y: usize) -> Color {
73 self.modules[y * self.width + x]
74 }
75
76 fn width(&self) -> usize {
77 self.width
78 }
79
80 fn height(&self) -> usize {
81 self.height
82 }
83
84 fn modules(&self) -> &[Color] {
85 self.modules
86 }
87}
88
89#[derive(Clone, Copy, Debug, PartialEq, Eq)]
95pub struct QrCodeRef<'a> {
96 modules: &'a [Color],
97 version: Version,
98 ec_level: EcLevel,
99 width: usize,
100}
101
102impl<'a> QrCodeRef<'a> {
103 #[must_use]
107 pub const fn new(modules: &'a [Color], width: usize, version: Version, ec_level: EcLevel) -> Option<Self> {
108 if !has_valid_module_geometry(modules.len(), width, width) {
109 return None;
110 }
111 Some(Self { modules, version, ec_level, width })
112 }
113
114 #[must_use]
116 pub const fn module_view(self) -> ModuleView<'a> {
117 ModuleView { modules: self.modules, width: self.width, height: self.width }
118 }
119}
120
121impl ModuleSource for QrCodeRef<'_> {
122 fn get(&self, x: usize, y: usize) -> Color {
123 self.modules[y * self.width + x]
124 }
125
126 fn width(&self) -> usize {
127 self.width
128 }
129
130 fn height(&self) -> usize {
131 self.width
132 }
133
134 fn modules(&self) -> &[Color] {
135 self.modules
136 }
137}
138
139impl QrSymbol for QrCodeRef<'_> {
140 fn version(&self) -> Version {
141 self.version
142 }
143
144 fn error_correction_level(&self) -> EcLevel {
145 self.ec_level
146 }
147}
148
149pub trait Encoder {
155 type Output;
157
158 type Error;
160
161 fn encode(&self, input: &[u8]) -> Result<Self::Output, Self::Error>;
167}
168
169pub trait Builder {
174 type Output;
176
177 type Error;
179
180 fn build(self) -> Result<Self::Output, Self::Error>;
186}
187
188pub trait Renderer<Code: ModuleSource + ?Sized> {
193 type Output;
195
196 type Error;
198
199 fn render(&self, code: &Code) -> Result<Self::Output, Self::Error>;
205}
206
207pub trait ModuleSource {
212 fn get(&self, x: usize, y: usize) -> Color;
218
219 fn width(&self) -> usize;
221
222 fn height(&self) -> usize;
224
225 fn modules(&self) -> &[Color];
227
228 fn row(&self, y: usize) -> &[Color] {
235 let width = self.width();
236 let start = y * width;
237 &self.modules()[start..start + width]
238 }
239
240 fn is_empty(&self) -> bool {
242 self.width() == 0 || self.height() == 0
243 }
244}
245
246pub trait QrSymbol: ModuleSource {
252 fn version(&self) -> Version;
254
255 fn error_correction_level(&self) -> EcLevel;
257
258 fn quiet_zone(&self) -> u32 {
262 if self.version().is_micro() { 2 } else { 4 }
263 }
264}
265
266pub trait ModuleStorage {
272 fn get(&self, x: usize, y: usize) -> Color;
278
279 fn set(&mut self, x: usize, y: usize, color: Color);
285
286 fn width(&self) -> usize;
288
289 fn height(&self) -> usize;
291
292 fn modules(&self) -> &[Color];
294
295 fn is_empty(&self) -> bool {
297 self.width() == 0 || self.height() == 0
298 }
299}
300
301impl<T: ModuleStorage + ?Sized> ModuleSource for T {
302 fn get(&self, x: usize, y: usize) -> Color {
303 ModuleStorage::get(self, x, y)
304 }
305
306 fn width(&self) -> usize {
307 ModuleStorage::width(self)
308 }
309
310 fn height(&self) -> usize {
311 ModuleStorage::height(self)
312 }
313
314 fn modules(&self) -> &[Color] {
315 ModuleStorage::modules(self)
316 }
317
318 fn is_empty(&self) -> bool {
319 ModuleStorage::is_empty(self)
320 }
321}
322
323#[cfg(test)]
324mod tests {
325 use super::{Builder, Encoder, ModuleSource, ModuleStorage, ModuleView, QrCodeRef, QrSymbol, Renderer};
326 use crate::{Color, EcLevel, Version};
327 use core::convert::Infallible;
328
329 struct DummySymbol {
330 version: Version,
331 modules: [Color; 1],
332 }
333
334 impl ModuleSource for DummySymbol {
335 fn get(&self, _x: usize, _y: usize) -> Color {
336 self.modules[0]
337 }
338
339 fn width(&self) -> usize {
340 1
341 }
342
343 fn height(&self) -> usize {
344 1
345 }
346
347 fn modules(&self) -> &[Color] {
348 &self.modules
349 }
350 }
351
352 impl QrSymbol for DummySymbol {
353 fn version(&self) -> Version {
354 self.version
355 }
356
357 fn error_correction_level(&self) -> EcLevel {
358 EcLevel::M
359 }
360 }
361
362 struct DummyBuilder {
363 value: u8,
364 }
365
366 impl Builder for DummyBuilder {
367 type Output = u8;
368 type Error = ();
369
370 fn build(self) -> Result<Self::Output, Self::Error> {
371 Ok(self.value)
372 }
373 }
374
375 struct DummyEncoder;
376
377 impl Encoder for DummyEncoder {
378 type Output = usize;
379 type Error = Infallible;
380
381 fn encode(&self, input: &[u8]) -> Result<Self::Output, Self::Error> {
382 Ok(input.len())
383 }
384 }
385
386 struct DummyRenderer {
387 dark: char,
388 light: char,
389 }
390
391 impl<C: ModuleSource + ?Sized> Renderer<C> for DummyRenderer {
392 type Output = String;
393 type Error = Infallible;
394
395 fn render(&self, code: &C) -> Result<Self::Output, Self::Error> {
396 let mut out = String::new();
397 for y in 0..code.height() {
398 for x in 0..code.width() {
399 out.push(match code.get(x, y) {
400 Color::Dark => self.dark,
401 Color::Light => self.light,
402 });
403 }
404 }
405 Ok(out)
406 }
407 }
408
409 struct DummyStorage {
410 modules: [Color; 4],
411 width: usize,
412 }
413
414 impl ModuleStorage for DummyStorage {
415 fn get(&self, x: usize, y: usize) -> Color {
416 self.modules[y * self.width + x]
417 }
418
419 fn set(&mut self, x: usize, y: usize, color: Color) {
420 self.modules[y * self.width + x] = color;
421 }
422
423 fn width(&self) -> usize {
424 self.width
425 }
426
427 fn height(&self) -> usize {
428 self.modules.len() / self.width
429 }
430
431 fn modules(&self) -> &[Color] {
432 &self.modules
433 }
434 }
435
436 #[test]
437 fn module_view_reads_row_major_modules() {
438 let modules = [Color::Dark, Color::Light, Color::Light, Color::Dark];
439 let view = ModuleView::new(&modules, 2).unwrap();
440
441 assert_eq!(view.width(), 2);
442 assert_eq!(view.height(), 2);
443 assert_eq!(view.modules(), modules);
444 assert_eq!(view.row(1), &[Color::Light, Color::Dark]);
445 assert_eq!(view.get(0, 0), Color::Dark);
446 assert_eq!(view.get(1, 1), Color::Dark);
447 }
448
449 #[test]
450 fn module_view_reads_rectangular_row_major_modules() {
451 let modules = [Color::Dark, Color::Light, Color::Light, Color::Dark, Color::Dark, Color::Light];
452 let view = ModuleView::new_rect(&modules, 3, 2).unwrap();
453
454 assert_eq!(view.width(), 3);
455 assert_eq!(view.height(), 2);
456 assert_eq!(view.row(1), &[Color::Dark, Color::Dark, Color::Light]);
457 }
458
459 #[test]
460 fn module_view_row_range_borrows_contiguous_rows() {
461 let modules = [
462 Color::Dark,
463 Color::Light,
464 Color::Light,
465 Color::Dark,
466 Color::Dark,
467 Color::Light,
468 Color::Light,
469 Color::Dark,
470 Color::Dark,
471 ];
472 let view = ModuleView::new(&modules, 3).unwrap();
473 let rows = view.row_range(1, 3).unwrap();
474
475 assert_eq!(rows.width(), 3);
476 assert_eq!(rows.height(), 2);
477 assert_eq!(rows.modules(), &modules[3..9]);
478 assert!(view.row_range(2, 2).is_none());
479 assert!(view.row_range(2, 4).is_none());
480 }
481
482 #[test]
483 fn module_view_rejects_non_square_input() {
484 let modules = [Color::Dark, Color::Light, Color::Dark];
485
486 assert!(ModuleView::new(&modules, 2).is_none());
487 assert!(ModuleView::new(&modules, 0).is_none());
488 }
489
490 #[test]
491 fn module_view_rejects_invalid_rectangular_input() {
492 let modules = [Color::Dark, Color::Light, Color::Dark];
493
494 assert!(ModuleView::new_rect(&modules, 2, 2).is_none());
495 assert!(ModuleView::new_rect(&modules, 0, 2).is_none());
496 assert!(ModuleView::new_rect(&modules, 2, 0).is_none());
497 }
498
499 #[test]
500 fn module_views_reject_overflowing_geometry() {
501 assert!(ModuleView::new_rect(&[], usize::MAX, 2).is_none());
502 assert!(QrCodeRef::new(&[], usize::MAX, Version::Normal(1), EcLevel::L).is_none());
503 }
504
505 #[test]
506 fn qr_code_ref_exposes_borrowed_symbol_metadata() {
507 let modules = [Color::Dark, Color::Light, Color::Light, Color::Dark];
508 let symbol = QrCodeRef::new(&modules, 2, Version::Normal(3), EcLevel::Q).unwrap();
509
510 assert_eq!(symbol.width(), 2);
511 assert_eq!(symbol.height(), 2);
512 assert_eq!(symbol.modules(), modules);
513 assert_eq!(symbol.get(0, 0), Color::Dark);
514 assert_eq!(symbol.version(), Version::Normal(3));
515 assert_eq!(symbol.error_correction_level(), EcLevel::Q);
516 assert_eq!(symbol.quiet_zone(), 4);
517 }
518
519 #[test]
520 fn qr_code_ref_rejects_invalid_module_geometry() {
521 let modules = [Color::Dark, Color::Light, Color::Dark];
522
523 assert!(QrCodeRef::new(&modules, 2, Version::Normal(1), EcLevel::M).is_none());
524 assert!(QrCodeRef::new(&modules, 0, Version::Normal(1), EcLevel::M).is_none());
525 }
526
527 #[test]
528 fn qr_code_ref_module_view_reuses_borrowed_modules() {
529 let modules = [Color::Dark, Color::Light, Color::Light, Color::Dark];
530 let symbol = QrCodeRef::new(&modules, 2, Version::Micro(1), EcLevel::L).unwrap();
531 let view = symbol.module_view();
532
533 assert_eq!(view.modules(), modules);
534 assert_eq!(view.get(1, 1), Color::Dark);
535 assert_eq!(symbol.quiet_zone(), 2);
536 }
537
538 #[test]
539 fn qr_symbol_default_quiet_zone_for_normal_qr_is_four_modules() {
540 let symbol = DummySymbol { version: Version::Normal(1), modules: [Color::Dark] };
541
542 assert_eq!(symbol.quiet_zone(), 4);
543 }
544
545 #[test]
546 fn qr_symbol_default_quiet_zone_for_micro_qr_is_two_modules() {
547 let symbol = DummySymbol { version: Version::Micro(1), modules: [Color::Dark] };
548
549 assert_eq!(symbol.quiet_zone(), 2);
550 }
551
552 #[test]
553 fn builder_trait_builds_configured_output() {
554 let result = DummyBuilder { value: 7 }.build();
555
556 assert_eq!(result, Ok(7));
557 }
558
559 #[test]
560 fn encoder_trait_accepts_third_party_implementations() {
561 let output = DummyEncoder.encode(b"hello").unwrap();
562
563 assert_eq!(output, 5);
564 }
565
566 #[test]
567 fn renderer_trait_accepts_third_party_implementations() {
568 let modules = [Color::Dark, Color::Light, Color::Light, Color::Dark];
569 let view = ModuleView::new(&modules, 2).unwrap();
570 let renderer = DummyRenderer { dark: '#', light: '.' };
571
572 assert_eq!(renderer.render(&view).unwrap(), "#..#");
573 }
574
575 #[test]
576 fn module_storage_blanket_impl_provides_module_source() {
577 let mut storage = DummyStorage { modules: [Color::Light; 4], width: 2 };
578 storage.set(1, 0, Color::Dark);
579 storage.set(0, 1, Color::Dark);
580
581 assert_eq!(ModuleSource::width(&storage), 2);
582 assert_eq!(ModuleSource::height(&storage), 2);
583 assert_eq!(ModuleSource::get(&storage, 1, 0), Color::Dark);
584 assert_eq!(<DummyStorage as ModuleSource>::row(&storage, 1), &[Color::Dark, Color::Light]);
585 assert_eq!(ModuleSource::modules(&storage), &[Color::Light, Color::Dark, Color::Dark, Color::Light]);
586 }
587}