Skip to main content

qrcode_rs/
lib.rs

1//! QRCode encoder
2//!
3//! This crate provides a QR code and Micro QR code encoder for binary data.
4//!
5#![cfg_attr(feature = "image", doc = "```rust")]
6#![cfg_attr(not(feature = "image"), doc = "```ignore")]
7//! use qrcode_rs::QrCode;
8//! use image::Luma;
9//!
10//! // Encode some data into bits.
11//! let code = QrCode::new(b"01234567").unwrap();
12//!
13//! // Render the bits into an image.
14//! let image = code.render::<Luma<u8>>().build();
15//!
16//! // Save the image.
17//! # if cfg!(unix) {
18//! image.save("/tmp/qrcode.png").unwrap();
19//! # }
20//!
21//! // You can also render it into a string.
22//! let string = code.render()
23//!     .light_color(' ')
24//!     .dark_color('#')
25//!     .build();
26//! println!("{}", string);
27//! ```
28
29#![cfg_attr(docsrs, feature(doc_cfg))]
30#![cfg_attr(not(feature = "std"), no_std)]
31#![deny(missing_docs)]
32#![deny(clippy::uninlined_format_args, clippy::manual_range_contains, clippy::semicolon_if_nothing_returned)]
33#![allow(
34    clippy::must_use_candidate, // This is just annoying.
35)]
36
37extern crate alloc;
38
39pub mod render;
40pub mod structured_append;
41
42// The encoding primitive layer lives in `qrcode-core` and is re-exported here
43// so the public API (`qrcode_rs::bits::Bits`, `qrcode_rs::Version`, …) is unchanged.
44pub use qrcode_core::ConstVersion;
45pub use qrcode_core::{
46    AlphanumericMode, ByteMode, DynEncoder, DynRenderer, EncodeConfig, EncodedOutput, EncoderFactory, EncodingMode,
47    KanjiMode, ModuleGrid, NumericMode, PluginError, PluginRegistry, PostProcessor, QrPlugin, RenderConfig,
48    RenderOutput, RendererFactory, StaticVersion,
49};
50pub use qrcode_core::{bits, canvas, ec, optimize, plugin, traits, types};
51pub use qrcode_decode as decode;
52pub use qrcode_parse as parse;
53// `cast` stays crate-private (not part of the public API); re-import it so
54// `crate::cast::As` keeps resolving across the facade and render modules.
55pub use crate::types::{Color, EcLevel, Mode, QrError, QrResult, Version};
56pub use qrcode_core::QrCodeRef;
57use qrcode_core::cast;
58pub use qrcode_core::traits::{
59    Builder, Encoder, ModuleSource, ModuleStorage, ModuleView, QrSymbol, Renderer as CoreRenderer,
60};
61
62#[cfg(not(feature = "std"))]
63#[allow(unused_imports)]
64use alloc::{
65    borrow::ToOwned,
66    format,
67    string::{String, ToString},
68    vec,
69    vec::Vec,
70};
71
72use crate::cast::As;
73use crate::render::{Pixel, Renderer};
74use core::iter::FusedIterator;
75use core::ops::Index;
76
77/// The encoded QR code symbol.
78///
79/// `QrCode` is `Send + Sync`, so it can be shared or moved across threads
80/// (e.g. for parallel rendering of many codes). This is verified at compile
81/// time below.
82#[derive(Clone)]
83pub struct QrCode {
84    content: Vec<Color>,
85    version: Version,
86    ec_level: EcLevel,
87    width: usize,
88}
89
90/// Borrowed plugin view for a QR code.
91///
92/// This is a convenience wrapper around [`QrCode::render_with`] for code that
93/// wants to bind a symbol and registry once, then render through one or more
94/// named plugin renderers.
95pub struct QrCodePlugins<'a> {
96    code: &'a QrCode,
97    registry: &'a PluginRegistry,
98}
99
100impl QrCodePlugins<'_> {
101    /// Returns the QR code bound to this plugin view.
102    #[must_use]
103    pub fn code(&self) -> &QrCode {
104        self.code
105    }
106
107    /// Returns the plugin registry bound to this plugin view.
108    #[must_use]
109    pub fn registry(&self) -> &PluginRegistry {
110        self.registry
111    }
112
113    /// Renders the bound QR code through a named plugin renderer.
114    ///
115    /// # Errors
116    ///
117    /// Returns [`PluginError::RendererNotFound`] when no renderer is registered
118    /// with `renderer_name`, or another [`PluginError`] from grid construction,
119    /// postprocessing, or rendering.
120    pub fn render(&self, renderer_name: &str, config: &RenderConfig) -> Result<RenderOutput, PluginError> {
121        self.code.render_with(self.registry, renderer_name, config)
122    }
123}
124
125// Compile-time guarantee that QrCode stays Send + Sync as fields evolve.
126const _: () = {
127    const fn assert_send_sync<T: Send + Sync>() {}
128    assert_send_sync::<QrCode>();
129};
130
131impl QrCode {
132    /// Constructs a new QR code which automatically encodes the given data.
133    ///
134    /// This method uses the "medium" error correction level and automatically
135    /// chooses the smallest QR code.
136    ///
137    ///     use qrcode_rs::QrCode;
138    ///
139    ///     let code = QrCode::new(b"Some data").unwrap();
140    ///
141    /// # Errors
142    ///
143    /// Returns error if the QR code cannot be constructed, e.g. when the data
144    /// is too long.
145    pub fn new<D: AsRef<[u8]>>(data: D) -> QrResult<Self> {
146        AutoEncoder::default().encode(data.as_ref())
147    }
148
149    /// Constructs a new QR code which automatically encodes the given data at a
150    /// specific error correction level.
151    ///
152    /// This method automatically chooses the smallest QR code.
153    ///
154    ///     use qrcode_rs::{QrCode, EcLevel};
155    ///
156    ///     let code = QrCode::with_error_correction_level(b"Some data", EcLevel::H).unwrap();
157    ///
158    /// # Errors
159    ///
160    /// Returns error if the QR code cannot be constructed, e.g. when the data
161    /// is too long.
162    pub fn with_error_correction_level<D: AsRef<[u8]>>(data: D, ec_level: EcLevel) -> QrResult<Self> {
163        AutoEncoder::new(ec_level).encode(data.as_ref())
164    }
165
166    /// Constructs a new Micro QR code which automatically encodes the given
167    /// data.
168    ///
169    /// This method uses the "medium" error correction level and automatically
170    /// chooses the smallest Micro QR code.
171    ///
172    ///     use qrcode_rs::QrCode;
173    ///
174    ///     let code = QrCode::new_micro(b"123").unwrap();
175    ///
176    /// # Errors
177    ///
178    /// Returns error if the data cannot be encoded as a Micro QR code, e.g.
179    /// when the data is too long.
180    pub fn new_micro<D: AsRef<[u8]>>(data: D) -> QrResult<Self> {
181        MicroEncoder::default().encode(data.as_ref())
182    }
183
184    /// Constructs a new Micro QR code which automatically encodes the given
185    /// data at a specific error correction level.
186    ///
187    /// This method automatically chooses the smallest Micro QR code.
188    ///
189    ///     use qrcode_rs::{QrCode, EcLevel};
190    ///
191    ///     let code = QrCode::micro_with_error_correction_level(b"123", EcLevel::L).unwrap();
192    ///
193    /// # Errors
194    ///
195    /// Returns error if the data cannot be encoded as a Micro QR code, e.g.
196    /// when the data is too long, or when the error correction level is not
197    /// supported by any Micro QR version.
198    pub fn micro_with_error_correction_level<D: AsRef<[u8]>>(data: D, ec_level: EcLevel) -> QrResult<Self> {
199        MicroEncoder::new(ec_level).encode(data.as_ref())
200    }
201
202    /// Constructs a new QR code for the given version and error correction
203    /// level.
204    ///
205    ///     use qrcode_rs::{QrCode, Version, EcLevel};
206    ///
207    ///     let code = QrCode::with_version(b"Some data", Version::Normal(5), EcLevel::M).unwrap();
208    ///
209    /// This method can also be used to generate Micro QR code.
210    ///
211    ///     use qrcode_rs::{QrCode, Version, EcLevel};
212    ///
213    ///     let micro_code = QrCode::with_version(b"123", Version::Micro(1), EcLevel::L).unwrap();
214    ///
215    /// # Errors
216    ///
217    /// Returns error if the QR code cannot be constructed, e.g. when the data
218    /// is too long, or when the version and error correction level are
219    /// incompatible.
220    pub fn with_version<D: AsRef<[u8]>>(data: D, version: Version, ec_level: EcLevel) -> QrResult<Self> {
221        VersionEncoder::new(version, ec_level).encode(data.as_ref())
222    }
223
224    /// Constructs a new QR code with a compile-time checked normal QR version.
225    ///
226    /// `N` must be in `1..=40`. Invalid values fail during const evaluation
227    /// when this fixed-version path is monomorphized.
228    ///
229    /// ```rust
230    /// use qrcode_rs::{EcLevel, QrCode, Version};
231    ///
232    /// let code = QrCode::with_const_version::<5, _>(b"Some data", EcLevel::M).unwrap();
233    /// assert_eq!(code.version(), Version::Normal(5));
234    /// ```
235    ///
236    /// ```compile_fail
237    /// use qrcode_rs::{EcLevel, QrCode};
238    ///
239    /// let _ = QrCode::with_const_version::<41, _>(b"Some data", EcLevel::M);
240    /// ```
241    ///
242    /// # Errors
243    ///
244    /// Returns error if the QR code cannot be constructed for version `N`, e.g.
245    /// when the data is too long for that version and error correction level.
246    pub fn with_const_version<const N: i16, D: AsRef<[u8]>>(data: D, ec_level: EcLevel) -> QrResult<Self> {
247        ConstVersionEncoder::<N>::new(ec_level).encode(data.as_ref())
248    }
249
250    fn encode_auto(data: &[u8], ec_level: EcLevel) -> QrResult<Self> {
251        let bits = bits::encode_auto(data, ec_level)?;
252        Self::with_bits(bits, ec_level)
253    }
254
255    fn encode_auto_micro(data: &[u8], ec_level: EcLevel) -> QrResult<Self> {
256        let bits = bits::encode_auto_micro(data, ec_level)?;
257        Self::with_bits(bits, ec_level)
258    }
259
260    fn encode_with_version(data: &[u8], version: Version, ec_level: EcLevel) -> QrResult<Self> {
261        let mut bits = bits::Bits::new(version);
262        bits.push_optimal_data(data)?;
263        bits.push_terminator(ec_level)?;
264        Self::with_bits(bits, ec_level)
265    }
266
267    /// Constructs a new QR code with encoded bits.
268    ///
269    /// Use this method only if there are very special need to manipulate the
270    /// raw bits before encoding. Some examples are:
271    ///
272    /// * Encode data using specific character set with ECI
273    /// * Use the FNC1 modes
274    /// * Avoid the optimal segmentation algorithm
275    ///
276    /// See the `Bits` structure for detail.
277    ///
278    ///     #![allow(unused_must_use)]
279    ///
280    ///     use qrcode_rs::{QrCode, Version, EcLevel};
281    ///     use qrcode_rs::bits::Bits;
282    ///
283    ///     let mut bits = Bits::new(Version::Normal(1));
284    ///     bits.push_eci_designator(9);
285    ///     bits.push_byte_data(b"\xca\xfe\xe4\xe9\xea\xe1\xf2 QR");
286    ///     bits.push_terminator(EcLevel::L);
287    ///     let qrcode = QrCode::with_bits(bits, EcLevel::L);
288    ///
289    /// # Errors
290    ///
291    /// Returns error if the QR code cannot be constructed, e.g. when the bits
292    /// are too long, or when the version and error correction level are
293    /// incompatible.
294    pub fn with_bits(bits: bits::Bits, ec_level: EcLevel) -> QrResult<Self> {
295        let version = bits.version();
296        #[cfg(feature = "log")]
297        log::debug!("qrcode_rs: encoding at version {version:?}, ec {ec_level:?}");
298        let data = bits.into_bytes();
299        let (encoded_data, ec_data) = ec::construct_codewords(&data, version, ec_level)?;
300        let mut canvas = canvas::Canvas::new(version, ec_level);
301        canvas.draw_all_functional_patterns();
302        canvas.draw_data(&encoded_data, &ec_data);
303        let canvas = canvas.apply_best_mask();
304        let width = version.width().as_usize();
305        #[cfg(feature = "log")]
306        log::info!("qrcode_rs: encoded version {version:?} ec {ec_level:?} ({} modules)", width * width);
307        Ok(Self { content: canvas.into_colors(), version, ec_level, width })
308    }
309
310    /// Encodes many inputs at once at the given error-correction level, stopping
311    /// at the first input that fails to encode.
312    ///
313    /// # Examples
314    ///
315    /// ```rust
316    /// use qrcode_rs::{QrCode, EcLevel};
317    ///
318    /// let codes = QrCode::batch(&["alpha", "beta", "gamma"], EcLevel::M).unwrap();
319    /// assert_eq!(codes.len(), 3);
320    /// ```
321    pub fn batch<I, D>(inputs: I, ec_level: EcLevel) -> QrResult<Vec<Self>>
322    where
323        I: IntoIterator<Item = D>,
324        D: AsRef<[u8]>,
325    {
326        Self::stream_with_error_correction_level(inputs, ec_level).collect()
327    }
328
329    /// Lazily encodes inputs into QR codes with the default error-correction level.
330    ///
331    /// Unlike [`batch`](Self::batch), this returns an iterator and does not
332    /// collect every generated code into memory.
333    ///
334    /// # Examples
335    ///
336    /// ```rust
337    /// use qrcode_rs::{EcLevel, QrCode};
338    ///
339    /// let widths = QrCode::stream(["alpha", "beta"])
340    ///     .map(|code| code.map(|code| (code.error_correction_level(), code.width())))
341    ///     .collect::<Result<Vec<_>, _>>()
342    ///     .unwrap();
343    ///
344    /// assert_eq!(widths, vec![(EcLevel::M, 21), (EcLevel::M, 21)]);
345    /// ```
346    pub fn stream<I, D>(inputs: I) -> QrCodeStream<I::IntoIter>
347    where
348        I: IntoIterator<Item = D>,
349        D: AsRef<[u8]>,
350    {
351        Self::stream_with_error_correction_level(inputs, EcLevel::M)
352    }
353
354    /// Lazily encodes inputs into QR codes at `ec_level`.
355    ///
356    /// The iterator yields one [`QrResult<QrCode>`] per input, so callers can
357    /// stop at the first error with `collect::<Result<Vec<_>, _>>()` or handle
358    /// errors per item while keeping memory usage bounded by the active item.
359    pub fn stream_with_error_correction_level<I, D>(inputs: I, ec_level: EcLevel) -> QrCodeStream<I::IntoIter>
360    where
361        I: IntoIterator<Item = D>,
362        D: AsRef<[u8]>,
363    {
364        QrCodeStream { inputs: inputs.into_iter(), ec_level }
365    }
366
367    /// Gets the version of this QR code.
368    pub const fn version(&self) -> Version {
369        self.version
370    }
371
372    /// Gets the error correction level of this QR code.
373    pub const fn error_correction_level(&self) -> EcLevel {
374        self.ec_level
375    }
376
377    /// Gets the number of modules per side, i.e. the width of this QR code.
378    ///
379    /// The width here does not contain the quiet zone paddings.
380    pub const fn width(&self) -> usize {
381        self.width
382    }
383
384    /// Gets the maximum number of allowed erratic modules can be introduced
385    /// before the data becomes corrupted. Note that errors should not be
386    /// introduced to functional modules.
387    pub fn max_allowed_errors(&self) -> usize {
388        ec::max_allowed_errors(self.version, self.ec_level).expect("invalid version or ec_level")
389    }
390
391    /// Returns metadata about this QR code (version, error-correction level,
392    /// dimensions, module count, error tolerance, and data capacity).
393    ///
394    /// # Examples
395    ///
396    /// ```rust
397    /// use qrcode_rs::QrCode;
398    ///
399    /// let code = QrCode::new(b"hello").unwrap();
400    /// let info = code.info();
401    /// assert_eq!(info.width(), code.width());
402    /// assert_eq!(info.module_count(), code.width() * code.width());
403    /// assert!(info.data_capacity_bytes() > 0);
404    /// ```
405    #[must_use]
406    pub fn info(&self) -> Info {
407        Info {
408            version: self.version,
409            ec_level: self.ec_level,
410            width: self.width,
411            module_count: self.width * self.width,
412            max_allowed_errors: self.max_allowed_errors(),
413            data_capacity_bytes: bits::data_capacity_bits(self.version, self.ec_level).map(|b| b / 8).unwrap_or(0),
414        }
415    }
416
417    /// Returns diagnostic stats for this code: dark-module ratio and the split
418    /// between functional and data modules. Combine with [`QrCode::info`] for
419    /// version / capacity. Computed on demand (scans the grid).
420    ///
421    /// # Examples
422    ///
423    /// ```rust
424    /// use qrcode_rs::QrCode;
425    ///
426    /// let code = QrCode::new(b"hello").unwrap();
427    /// let a = code.analyze();
428    /// assert!(a.dark_ratio() > 0.0 && a.dark_ratio() < 1.0);
429    /// assert_eq!(a.functional_modules() + a.data_modules(), code.width() * code.width());
430    /// ```
431    #[must_use]
432    pub fn analyze(&self) -> Analysis {
433        let total = self.width * self.width;
434        let dark = self.content.iter().filter(|c| **c == Color::Dark).count();
435        let functional =
436            (0..self.width).map(|y| (0..self.width).filter(|x| self.is_functional(*x, y)).count()).sum::<usize>();
437        Analysis {
438            dark_ratio: if total == 0 { 0.0 } else { dark as f64 / total as f64 },
439            functional_modules: functional,
440            data_modules: total - functional,
441        }
442    }
443
444    /// Checks whether a module at coordinate (x, y) is a functional module or
445    /// not.
446    pub fn is_functional(&self, x: usize, y: usize) -> bool {
447        let x = x.try_into().expect("coordinate is too large for QR code");
448        let y = y.try_into().expect("coordinate is too large for QR code");
449        canvas::is_functional(self.version, self.version.width(), x, y)
450    }
451
452    /// Converts the QR code into a human-readable string. This is mainly for
453    /// debugging only.
454    pub fn to_debug_str(&self, on_char: char, off_char: char) -> String {
455        self.render().quiet_zone(false).dark_color(on_char).light_color(off_char).build()
456    }
457
458    /// Returns the module colors as a borrowed slice — no allocation. Use this
459    /// in preference to [`to_colors`](Self::to_colors) when you only need to
460    /// read the modules.
461    ///
462    /// The slice is row-major, with `width() * width()` entries and no quiet
463    /// zone.
464    ///
465    /// # Examples
466    ///
467    /// ```rust
468    /// use qrcode_rs::QrCode;
469    ///
470    /// let code = QrCode::new(b"hi").unwrap();
471    /// let colors = code.colors();
472    /// assert_eq!(colors.len(), code.width() * code.width());
473    /// ```
474    pub fn colors(&self) -> &[Color] {
475        &self.content
476    }
477
478    /// Returns a borrowed, read-only module-grid view.
479    ///
480    /// This is the facade-friendly [`ModuleSource`] adapter for APIs that accept
481    /// a borrowed QR module source without needing ownership of the full
482    /// [`QrCode`].
483    ///
484    /// # Examples
485    ///
486    /// ```rust
487    /// use qrcode_rs::{ModuleSource, QrCode};
488    ///
489    /// let code = QrCode::new(b"hi").unwrap();
490    /// let view = code.module_view();
491    /// assert_eq!(view.width(), code.width());
492    /// assert_eq!(view.modules(), code.colors());
493    /// ```
494    #[must_use]
495    pub fn module_view(&self) -> ModuleView<'_> {
496        ModuleView::new(&self.content, self.width).expect("QrCode stores a non-empty square module grid")
497    }
498
499    /// Returns a borrowed QR symbol view with module data and metadata.
500    ///
501    /// Unlike [`to_colors`](Self::to_colors), this does not allocate or clone
502    /// the module grid. It is useful for renderers and analyzers that accept a
503    /// [`QrSymbol`] and need version/error-correction metadata in addition to
504    /// read-only module access.
505    ///
506    /// # Examples
507    ///
508    /// ```rust
509    /// use qrcode_rs::{ModuleSource, QrCode, QrSymbol};
510    ///
511    /// let code = QrCode::new(b"hi").unwrap();
512    /// let borrowed = code.as_ref();
513    /// assert_eq!(borrowed.version(), code.version());
514    /// assert_eq!(borrowed.modules(), code.colors());
515    /// ```
516    #[must_use]
517    pub fn as_ref(&self) -> QrCodeRef<'_> {
518        QrCodeRef::new(&self.content, self.width, self.version, self.ec_level)
519            .expect("QrCode stores a non-empty square module grid")
520    }
521
522    /// Converts the QR code to a vector of colors.
523    pub fn to_colors(&self) -> Vec<Color> {
524        self.content.clone()
525    }
526
527    /// Converts the QR code to a vector of colors.
528    pub fn into_colors(self) -> Vec<Color> {
529        self.content
530    }
531
532    /// Renders the QR code into an image. The result is an image builder, which
533    /// you may do some additional configuration before copying it into a
534    /// concrete image.
535    ///  Note: the`image` crate itself also provides method to rotate the image,
536    /// or overlay a logo on top of the QR code.
537    /// # Examples
538    ///
539    #[cfg_attr(feature = "image", doc = " ```rust")]
540    #[cfg_attr(not(feature = "image"), doc = " ```ignore")]
541    /// # use qrcode_rs::QrCode;
542    /// # use image::Rgb;
543    ///
544    /// let image = QrCode::new(b"hello").unwrap()
545    ///                     .render()
546    ///                     .dark_color(Rgb([0, 0, 128]))
547    ///                     .light_color(Rgb([224, 224, 224])) // adjust colors
548    ///                     .quiet_zone(false)          // disable quiet zone (white border)
549    ///                     .min_dimensions(300, 300)   // sets minimum image size
550    ///                     .build();
551    /// ```
552    ///
553    pub fn render<P: Pixel>(&self) -> Renderer<'_, P> {
554        Renderer::from_symbol(self)
555    }
556
557    /// Returns the render builder for this QR code.
558    ///
559    /// This is an explicit builder-style alias for [`render`](Self::render),
560    /// useful when code wants the construction and rendering paths to read the
561    /// same way (`QrCode::builder(...).build()?.render_builder::<P>()...`).
562    ///
563    /// # Examples
564    ///
565    /// ```rust
566    /// use qrcode_rs::QrCode;
567    ///
568    /// let text = QrCode::new(b"hello").unwrap()
569    ///     .render_builder::<char>()
570    ///     .dark_color('#')
571    ///     .quiet_zone(false)
572    ///     .module_dimensions(1, 1)
573    ///     .build();
574    ///
575    /// assert!(text.contains('#'));
576    /// ```
577    pub fn render_builder<P: Pixel>(&self) -> Renderer<'_, P> {
578        self.render()
579    }
580
581    /// Renders the QR code on Tokio's blocking thread pool.
582    ///
583    /// This opt-in async helper is available with the `async` feature. It clones
584    /// the compact module grid and performs the synchronous render work inside
585    /// [`tokio::task::spawn_blocking`], so callers running inside a Tokio
586    /// runtime do not execute CPU-heavy rendering on the async worker thread.
587    ///
588    /// # Errors
589    ///
590    /// Returns [`tokio::task::JoinError`] if the blocking task is cancelled or
591    /// panics.
592    ///
593    /// # Examples
594    ///
595    /// ```rust
596    /// use qrcode_rs::QrCode;
597    ///
598    /// let code = QrCode::new(b"hello").unwrap();
599    /// let runtime = tokio::runtime::Builder::new_current_thread().build().unwrap();
600    /// let text = runtime.block_on(code.render_async::<char>()).unwrap();
601    /// assert!(!text.is_empty());
602    /// ```
603    #[cfg(feature = "async")]
604    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
605    pub async fn render_async<P>(&self) -> Result<P::Image, tokio::task::JoinError>
606    where
607        P: Pixel + Send + 'static,
608        P::Image: Send + 'static,
609    {
610        let code = self.clone();
611        tokio::task::spawn_blocking(move || code.render::<P>().build()).await
612    }
613
614    /// Encodes raw input through a named plugin encoder.
615    ///
616    /// This is the encoder-side counterpart to [`QrCode::render_with`]: it
617    /// looks up `encoder_name` in `registry`, builds the dynamic encoder with
618    /// `config`, and returns the encoder's type-erased output.
619    ///
620    /// # Errors
621    ///
622    /// Returns [`PluginError::EncoderNotFound`] when no encoder is registered
623    /// with `encoder_name`, or another [`PluginError`] returned by the encoder.
624    pub fn encode_with(
625        registry: &PluginRegistry,
626        encoder_name: &str,
627        input: &[u8],
628        config: &EncodeConfig,
629    ) -> Result<EncodedOutput, PluginError> {
630        registry.build_encoder(encoder_name, config)?.encode(input)
631    }
632
633    /// Renders the QR code through a named plugin renderer.
634    ///
635    /// The method looks up `renderer_name` in `registry`, copies this QR code's
636    /// module grid into a mutable [`ModuleGrid`], applies all registered
637    /// [`PostProcessor`] values in registration order, then renders the
638    /// transformed grid through the selected dynamic renderer.
639    ///
640    /// # Errors
641    ///
642    /// Returns [`PluginError::RendererNotFound`] when no renderer is registered
643    /// with `renderer_name`, or another [`PluginError`] from grid construction,
644    /// postprocessing, or rendering.
645    pub fn render_with(
646        &self,
647        registry: &PluginRegistry,
648        renderer_name: &str,
649        config: &RenderConfig,
650    ) -> Result<RenderOutput, PluginError> {
651        let mut modules = ModuleGrid::new(self.content.clone(), self.width, self.width)?;
652        registry.process_modules(&mut modules)?;
653        registry.build_renderer(renderer_name, config)?.render(&modules)
654    }
655
656    /// Binds this QR code to a plugin registry for fluent plugin rendering.
657    ///
658    /// The returned view borrows both values and delegates to
659    /// [`QrCode::render_with`], so it has the same deterministic, explicit
660    /// registry behavior without introducing global plugin state.
661    pub fn with_plugins<'a>(&'a self, registry: &'a PluginRegistry) -> QrCodePlugins<'a> {
662        QrCodePlugins { code: self, registry }
663    }
664}
665
666impl QrCode {
667    /// Creates a [`QrCodeBuilder`] for configuring and constructing a QR code.
668    ///
669    /// This is an ergonomic alternative to the `with_*` constructors. The
670    /// builder uses the same encoding paths, so its output is identical to the
671    /// equivalent constructor.
672    ///
673    /// # Examples
674    ///
675    /// ```rust
676    /// use qrcode_rs::{QrCode, EcLevel};
677    ///
678    /// let code = QrCode::builder(b"https://example.com")
679    ///     .ec_level(EcLevel::H)
680    ///     .build()
681    ///     .unwrap();
682    /// # let _ = code;
683    /// ```
684    pub fn builder<D: AsRef<[u8]>>(data: D) -> QrCodeBuilder<D> {
685        QrCodeBuilder::new(data)
686    }
687
688    /// Returns an iterator yielding one [`Row`] of modules at a time.
689    ///
690    /// Each row iterates over the module [`Color`]s from left to right. The
691    /// quiet zone is *not* included.
692    ///
693    /// # Examples
694    ///
695    /// ```rust
696    /// use qrcode_rs::QrCode;
697    ///
698    /// let code = QrCode::new(b"hi").unwrap();
699    /// for row in code.rows() {
700    ///     for color in row {
701    ///         # let _ = color;
702    ///     }
703    /// }
704    /// ```
705    pub fn rows(&self) -> Rows<'_> {
706        Rows { code: self, y: 0 }
707    }
708
709    /// Returns an iterator over the `(x, y)` coordinates of every dark module,
710    /// convenient for custom rendering. The quiet zone is *not* included.
711    ///
712    /// # Examples
713    ///
714    /// ```rust
715    /// use qrcode_rs::QrCode;
716    ///
717    /// let code = QrCode::new(b"hi").unwrap();
718    /// let dark_count = code.dark_modules().count();
719    /// # let _ = dark_count;
720    /// ```
721    pub fn dark_modules(&self) -> DarkModules<'_> {
722        DarkModules { code: self, idx: 0 }
723    }
724
725    /// Encodes a URL, using high error correction (robust to print damage).
726    ///
727    /// # Errors
728    ///
729    /// Returns an error only if the URL is too long to encode.
730    pub fn for_url<D: AsRef<[u8]>>(url: D) -> QrResult<Self> {
731        Self::with_error_correction_level(url, EcLevel::H)
732    }
733
734    /// Encodes plain text at the default (medium) error correction level.
735    ///
736    /// # Errors
737    ///
738    /// Returns an error only if the text is too long to encode.
739    pub fn for_text<D: AsRef<[u8]>>(text: D) -> QrResult<Self> {
740        Self::new(text)
741    }
742
743    /// Encodes a WiFi configuration that most phone cameras will offer to join.
744    ///
745    /// `auth` is one of `WPA`, `WEP` or `nopass`. Special characters in the
746    /// SSID/password are backslash-escaped per the WiFi QR specification.
747    ///
748    /// # Errors
749    ///
750    /// Returns an error if the resulting payload is too long to encode.
751    ///
752    /// # Examples
753    ///
754    /// ```rust
755    /// use qrcode_rs::QrCode;
756    ///
757    /// let code = QrCode::for_wifi("MyNetwork", "p\\a;ss", "WPA").unwrap();
758    /// # let _ = code;
759    /// ```
760    pub fn for_wifi(ssid: &str, password: &str, auth: &str) -> QrResult<Self> {
761        Self::new(parse::wifi::encode_wifi(ssid, password, auth))
762    }
763
764    /// Encodes a minimal vCard 3.0 contact card.
765    ///
766    /// # Errors
767    ///
768    /// Returns an error if the resulting payload is too long to encode.
769    ///
770    /// # Examples
771    ///
772    /// ```rust
773    /// use qrcode_rs::QrCode;
774    ///
775    /// let code = QrCode::for_vcard("John Doe", "+1234567890", "john@example.com").unwrap();
776    /// # let _ = code;
777    /// ```
778    pub fn for_vcard(name: &str, phone: &str, email: &str) -> QrResult<Self> {
779        Self::new(parse::vcard::encode_vcard(name, phone, email))
780    }
781
782    /// Encodes a GS1 data carrier (FNC1 in first position), e.g. a GTIN /
783    /// application-identifier payload such as
784    /// `"010491234512345915970331301234561842"`. Uses medium error correction
785    /// and the smallest fitting version.
786    ///
787    /// # Errors
788    ///
789    /// Returns an error if the data is too long to encode.
790    ///
791    /// # Examples
792    ///
793    /// ```rust
794    /// use qrcode_rs::QrCode;
795    ///
796    /// let code = QrCode::for_gs1("010491234512345915970331301234561842").unwrap();
797    /// # let _ = code;
798    /// ```
799    pub fn for_gs1<D: AsRef<[u8]>>(data: D) -> QrResult<Self> {
800        let data = data.as_ref();
801        for v in 1..=40 {
802            let version = Version::Normal(v);
803            let mut bits = bits::Bits::new(version);
804            if bits.push_fnc1_first_position().is_err()
805                || bits.push_optimal_data(data).is_err()
806                || bits.push_terminator(EcLevel::M).is_err()
807            {
808                continue;
809            }
810            return Self::with_bits(bits, EcLevel::M);
811        }
812        Err(QrError::DataTooLong)
813    }
814
815    /// Splits `payload` across `symbols` QR codes (2..=16) using Structured
816    /// Append (ISO/IEC 18004 §7.4), each at error-correction level `ec`. Every
817    /// symbol is the smallest version that fits its chunk plus the 20-bit
818    /// Structured Append header.
819    ///
820    /// This is a thin convenience over
821    /// [`crate::structured_append::StructuredAppend`]; see that type for the
822    /// split and parity details, and [`crate::structured_append::reassemble`]
823    /// for recombining decoded symbols.
824    ///
825    /// # Errors
826    ///
827    /// Returns [`QrError::InvalidStructuredAppend`] if `symbols` is not in
828    /// `2..=16`, or [`QrError::DataTooLong`] if a chunk cannot fit even version
829    /// 40 at `ec`.
830    ///
831    /// # Examples
832    ///
833    /// ```rust
834    /// use qrcode_rs::{EcLevel, QrCode};
835    ///
836    /// let codes = QrCode::structured_append(b"split across multiple symbols", 3, EcLevel::M)?;
837    /// assert_eq!(codes.len(), 3);
838    /// # Ok::<(), qrcode_rs::QrError>(())
839    /// ```
840    pub fn structured_append<D: AsRef<[u8]>>(payload: D, symbols: u8, ec: EcLevel) -> QrResult<Vec<Self>> {
841        let sa = structured_append::StructuredAppend::new(symbols, payload.as_ref())?;
842        sa.encode(ec)
843    }
844
845    /// Generates accessible alt text describing a QR code that encodes `data`.
846    ///
847    /// URLs are described as "linking to …"; other payloads as "containing: …".
848    /// Use the result as the `alt` of an `<img>` or the `aria-label` of an inline
849    /// SVG so assistive technology can describe the code without decoding it.
850    ///
851    /// This is an associated function (it does not require a constructed
852    /// [`QrCode`]), so the input data does not need to be retained on the code.
853    ///
854    /// # Examples
855    ///
856    /// ```rust
857    /// use qrcode_rs::QrCode;
858    ///
859    /// assert_eq!(QrCode::alt_text("https://example.com"), "QR code linking to https://example.com");
860    /// assert_eq!(QrCode::alt_text("hello"), "QR code containing: hello");
861    /// ```
862    #[must_use]
863    pub fn alt_text<D: AsRef<[u8]>>(data: D) -> String {
864        let text = String::from_utf8_lossy(data.as_ref());
865        if text.starts_with("http://") || text.starts_with("https://") {
866            format!("QR code linking to {text}")
867        } else {
868            format!("QR code containing: {text}")
869        }
870    }
871
872    /// Generates alt text with a custom formatter that receives the raw bytes.
873    ///
874    /// # Examples
875    ///
876    /// ```rust
877    /// use qrcode_rs::QrCode;
878    ///
879    /// let alt = QrCode::alt_text_custom("hello", |data| {
880    ///     format!("A QR code with {} bytes", data.len())
881    /// });
882    /// assert_eq!(alt, "A QR code with 5 bytes");
883    /// ```
884    #[must_use]
885    pub fn alt_text_custom<D: AsRef<[u8]>, F: FnOnce(&[u8]) -> String>(data: D, f: F) -> String {
886        f(data.as_ref())
887    }
888
889    /// Encodes `data` forced into a single `mode` at a pinned version. Used by
890    /// [`QrCodeBuilder::build`] when both a version and an encoding-mode hint
891    /// are set.
892    fn with_mode<D: AsRef<[u8]>>(data: D, version: Version, ec_level: EcLevel, mode: Mode) -> QrResult<Self> {
893        let mut bits = bits::Bits::new(version);
894        match mode {
895            Mode::Numeric => bits.push_numeric_data(data.as_ref())?,
896            Mode::Alphanumeric => bits.push_alphanumeric_data(data.as_ref())?,
897            Mode::Byte => bits.push_byte_data(data.as_ref())?,
898            Mode::Kanji => bits.push_kanji_data(data.as_ref())?,
899        }
900        bits.push_terminator(ec_level)?;
901        Self::with_bits(bits, ec_level)
902    }
903
904    /// Encodes `data` forced into a single `mode`, auto-selecting the smallest
905    /// fitting version. Used by [`QrCodeBuilder::build`] when an encoding-mode
906    /// hint is set without a pinned version. Returns the underlying error
907    /// (e.g. [`QrError::InvalidCharacter`]) if the data is incompatible with the
908    /// forced mode.
909    fn with_mode_auto<D: AsRef<[u8]>>(data: D, ec_level: EcLevel, mode: Mode) -> QrResult<Self> {
910        let data = data.as_ref();
911        let mut last_err = QrError::DataTooLong;
912        for v in 1..=40 {
913            let version = Version::Normal(v);
914            let mut bits = bits::Bits::new(version);
915            let pushed = match mode {
916                Mode::Numeric => bits.push_numeric_data(data),
917                Mode::Alphanumeric => bits.push_alphanumeric_data(data),
918                Mode::Byte => bits.push_byte_data(data),
919                Mode::Kanji => bits.push_kanji_data(data),
920            };
921            if let Err(e) = pushed {
922                last_err = e;
923                continue;
924            }
925            if let Err(e) = bits.push_terminator(ec_level) {
926                last_err = e;
927                continue;
928            }
929            return Self::with_bits(bits, ec_level);
930        }
931        Err(last_err)
932    }
933}
934
935impl Index<(usize, usize)> for QrCode {
936    type Output = Color;
937
938    fn index(&self, (x, y): (usize, usize)) -> &Color {
939        let index = y * self.width + x;
940        &self.content[index]
941    }
942}
943
944impl ModuleStorage for QrCode {
945    fn get(&self, x: usize, y: usize) -> Color {
946        self[(x, y)]
947    }
948
949    fn set(&mut self, x: usize, y: usize, color: Color) {
950        let index = y * self.width + x;
951        self.content[index] = color;
952    }
953
954    fn width(&self) -> usize {
955        self.width
956    }
957
958    fn height(&self) -> usize {
959        self.width
960    }
961
962    fn modules(&self) -> &[Color] {
963        self.colors()
964    }
965}
966
967impl QrSymbol for QrCode {
968    fn version(&self) -> Version {
969        self.version
970    }
971
972    fn error_correction_level(&self) -> EcLevel {
973        self.ec_level
974    }
975}
976
977//------------------------------------------------------------------------------
978//{{{ Encoder adapters
979
980/// Encoder adapter for automatically sized normal QR codes.
981///
982/// This is the trait-friendly counterpart to
983/// [`QrCode::with_error_correction_level`].
984#[derive(Clone, Copy, Debug, PartialEq, Eq)]
985pub struct AutoEncoder {
986    ec_level: EcLevel,
987}
988
989impl AutoEncoder {
990    /// Creates an encoder with the given error-correction level.
991    #[must_use]
992    pub const fn new(ec_level: EcLevel) -> Self {
993        Self { ec_level }
994    }
995
996    /// Returns the configured error-correction level.
997    #[must_use]
998    pub const fn ec_level(&self) -> EcLevel {
999        self.ec_level
1000    }
1001}
1002
1003impl Default for AutoEncoder {
1004    fn default() -> Self {
1005        Self { ec_level: EcLevel::M }
1006    }
1007}
1008
1009impl Encoder for AutoEncoder {
1010    type Output = QrCode;
1011    type Error = QrError;
1012
1013    fn encode(&self, input: &[u8]) -> QrResult<QrCode> {
1014        QrCode::encode_auto(input, self.ec_level)
1015    }
1016}
1017
1018/// Encoder adapter for automatically sized Micro QR codes.
1019///
1020/// This is the trait-friendly counterpart to
1021/// [`QrCode::micro_with_error_correction_level`].
1022#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1023pub struct MicroEncoder {
1024    ec_level: EcLevel,
1025}
1026
1027impl MicroEncoder {
1028    /// Creates a Micro QR encoder with the given error-correction level.
1029    #[must_use]
1030    pub const fn new(ec_level: EcLevel) -> Self {
1031        Self { ec_level }
1032    }
1033
1034    /// Returns the configured error-correction level.
1035    #[must_use]
1036    pub const fn ec_level(&self) -> EcLevel {
1037        self.ec_level
1038    }
1039}
1040
1041impl Default for MicroEncoder {
1042    fn default() -> Self {
1043        Self { ec_level: EcLevel::M }
1044    }
1045}
1046
1047impl Encoder for MicroEncoder {
1048    type Output = QrCode;
1049    type Error = QrError;
1050
1051    fn encode(&self, input: &[u8]) -> QrResult<QrCode> {
1052        QrCode::encode_auto_micro(input, self.ec_level)
1053    }
1054}
1055
1056/// Encoder adapter for a pinned [`Version`] and [`EcLevel`].
1057///
1058/// This is the trait-friendly counterpart to [`QrCode::with_version`].
1059#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1060pub struct VersionEncoder {
1061    version: Version,
1062    ec_level: EcLevel,
1063}
1064
1065impl VersionEncoder {
1066    /// Creates an encoder for a specific version and error-correction level.
1067    #[must_use]
1068    pub const fn new(version: Version, ec_level: EcLevel) -> Self {
1069        Self { version, ec_level }
1070    }
1071
1072    /// Returns the configured version.
1073    #[must_use]
1074    pub const fn version(&self) -> Version {
1075        self.version
1076    }
1077
1078    /// Returns the configured error-correction level.
1079    #[must_use]
1080    pub const fn ec_level(&self) -> EcLevel {
1081        self.ec_level
1082    }
1083}
1084
1085impl Encoder for VersionEncoder {
1086    type Output = QrCode;
1087    type Error = QrError;
1088
1089    fn encode(&self, input: &[u8]) -> QrResult<QrCode> {
1090        QrCode::encode_with_version(input, self.version, self.ec_level)
1091    }
1092}
1093
1094/// Encoder adapter for a compile-time checked normal QR version and [`EcLevel`].
1095///
1096/// This is the trait-friendly counterpart to
1097/// [`QrCode::with_const_version`]. `N` must be in `1..=40`.
1098///
1099/// ```rust
1100/// use qrcode_rs::{ConstVersionEncoder, EcLevel, Encoder, Version};
1101///
1102/// let code = ConstVersionEncoder::<5>::new(EcLevel::M).encode(b"Some data").unwrap();
1103/// assert_eq!(code.version(), Version::Normal(5));
1104/// ```
1105///
1106/// ```compile_fail
1107/// use qrcode_rs::{ConstVersionEncoder, EcLevel};
1108///
1109/// const INVALID: ConstVersionEncoder<0> = ConstVersionEncoder::<0>::new(EcLevel::M);
1110/// ```
1111#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1112pub struct ConstVersionEncoder<const N: i16> {
1113    ec_level: EcLevel,
1114}
1115
1116impl<const N: i16> ConstVersionEncoder<N> {
1117    /// Creates an encoder for fixed normal QR version `N`.
1118    #[must_use]
1119    pub const fn new(ec_level: EcLevel) -> Self {
1120        let _ = ConstVersion::<N>::VALUE;
1121        Self { ec_level }
1122    }
1123
1124    /// Returns the compile-time checked dynamic version value.
1125    #[must_use]
1126    pub const fn version(&self) -> Version {
1127        ConstVersion::<N>::VALUE
1128    }
1129
1130    /// Returns the configured error-correction level.
1131    #[must_use]
1132    pub const fn ec_level(&self) -> EcLevel {
1133        self.ec_level
1134    }
1135}
1136
1137impl<const N: i16> Encoder for ConstVersionEncoder<N> {
1138    type Output = QrCode;
1139    type Error = QrError;
1140
1141    fn encode(&self, input: &[u8]) -> QrResult<QrCode> {
1142        QrCode::encode_with_version(input, ConstVersion::<N>::VALUE, self.ec_level)
1143    }
1144}
1145
1146//}}}
1147
1148//------------------------------------------------------------------------------
1149//{{{ QrCodeBuilder
1150
1151/// A builder for [`QrCode`], offering ergonomic, chainable configuration.
1152///
1153/// Construct one with [`QrCode::builder`]. The builder delegates to the
1154/// existing constructors, so its output is identical to calling them directly.
1155#[derive(Clone, Debug)]
1156pub struct QrCodeBuilder<D: AsRef<[u8]>> {
1157    data: D,
1158    ec_level: EcLevel,
1159    version: Option<Version>,
1160    micro: bool,
1161    mode_hint: Option<Mode>,
1162}
1163
1164impl<D: AsRef<[u8]>> QrCodeBuilder<D> {
1165    fn new(data: D) -> Self {
1166        Self { data, ec_level: EcLevel::M, version: None, micro: false, mode_hint: None }
1167    }
1168
1169    /// Sets the error correction level (default [`EcLevel::M`]).
1170    #[must_use]
1171    pub fn ec_level(mut self, ec_level: EcLevel) -> Self {
1172        self.ec_level = ec_level;
1173        self
1174    }
1175
1176    /// Pins a specific QR [`Version`]. When set, `build()` behaves like
1177    /// [`QrCode::with_version`]. If [`micro`](Self::micro) is also set, the
1178    /// explicit version takes precedence.
1179    #[must_use]
1180    pub fn version(mut self, version: Version) -> Self {
1181        self.version = Some(version);
1182        self
1183    }
1184
1185    /// Requests a Micro QR code (the smallest fitting Micro version), behaving
1186    /// like [`QrCode::micro_with_error_correction_level`] when no explicit
1187    /// [`version`](Self::version) is set.
1188    #[must_use]
1189    pub fn micro(mut self, yes: bool) -> Self {
1190        self.micro = yes;
1191        self
1192    }
1193
1194    /// Hints the encoding [`Mode`] (e.g. [`Mode::Byte`]), bypassing automatic
1195    /// mode optimization. When a [`version`](Self::version) is also set it is
1196    /// used directly; otherwise the smallest fitting version for that mode is
1197    /// auto-selected.
1198    ///
1199    /// The data must be encodable in the chosen mode: [`Mode::Kanji`] validates
1200    /// its Shift-JIS pairs and [`Mode::Byte`] accepts anything, but
1201    /// [`Mode::Numeric`] / [`Mode::Alphanumeric`] assume their input already
1202    /// matches (as automatic optimization would never select them otherwise).
1203    #[must_use]
1204    pub fn encoding_mode(mut self, mode: Mode) -> Self {
1205        self.mode_hint = Some(mode);
1206        self
1207    }
1208
1209    /// Hints the encoding mode with a type-level [`EncodingMode`] marker.
1210    ///
1211    /// Unlike [`encoding_mode`](Self::encoding_mode), this validates `data`
1212    /// immediately and returns [`QrError::InvalidCharacter`] when the selected
1213    /// mode cannot represent it.
1214    ///
1215    /// ```rust
1216    /// use qrcode_rs::{NumericMode, QrCode, QrError, Version};
1217    ///
1218    /// let code = QrCode::builder(b"01234567")
1219    ///     .version(Version::Normal(1))
1220    ///     .encoding_mode_typed::<NumericMode>()
1221    ///     .unwrap()
1222    ///     .build()
1223    ///     .unwrap();
1224    /// assert_eq!(code.version(), Version::Normal(1));
1225    ///
1226    /// let err = QrCode::builder(b"12a")
1227    ///     .encoding_mode_typed::<NumericMode>()
1228    ///     .unwrap_err();
1229    /// assert_eq!(err, QrError::InvalidCharacter { position: 2, byte: b'a' });
1230    /// ```
1231    ///
1232    /// # Errors
1233    ///
1234    /// Returns [`QrError::InvalidCharacter`] with the first invalid byte when
1235    /// `data` is not valid for `M`.
1236    pub fn encoding_mode_typed<M: EncodingMode>(mut self) -> QrResult<Self> {
1237        if let Some((position, byte)) = M::invalid_character(self.data.as_ref()) {
1238            return Err(QrError::InvalidCharacter { position, byte });
1239        }
1240        self.mode_hint = Some(M::MODE);
1241        Ok(self)
1242    }
1243
1244    /// Forces a specific encoding [`Mode`], bypassing automatic optimization.
1245    /// This is an alias for [`encoding_mode`](Self::encoding_mode), provided for
1246    /// familiarity with the QR-code vocabulary.
1247    #[must_use]
1248    pub fn force_mode(self, mode: Mode) -> Self {
1249        self.encoding_mode(mode)
1250    }
1251
1252    /// Forces a type-level encoding mode after validating the input.
1253    ///
1254    /// This is an alias for [`encoding_mode_typed`](Self::encoding_mode_typed).
1255    ///
1256    /// # Errors
1257    ///
1258    /// Returns [`QrError::InvalidCharacter`] with the first invalid byte when
1259    /// `data` is not valid for `M`.
1260    pub fn force_mode_typed<M: EncodingMode>(self) -> QrResult<Self> {
1261        self.encoding_mode_typed::<M>()
1262    }
1263
1264    /// Builds the [`QrCode`].
1265    ///
1266    /// # Errors
1267    ///
1268    /// Propagates any [`QrError`] from the underlying encoder
1269    /// (e.g. data too long, or an incompatible version / error-correction
1270    /// combination).
1271    pub fn build(self) -> QrResult<QrCode> {
1272        if let Some(version) = self.version {
1273            if let Some(mode) = self.mode_hint {
1274                return QrCode::with_mode(self.data, version, self.ec_level, mode);
1275            }
1276            return QrCode::with_version(self.data, version, self.ec_level);
1277        }
1278        if let Some(mode) = self.mode_hint {
1279            return QrCode::with_mode_auto(self.data, self.ec_level, mode);
1280        }
1281        if self.micro {
1282            return QrCode::micro_with_error_correction_level(self.data, self.ec_level);
1283        }
1284        QrCode::with_error_correction_level(self.data, self.ec_level)
1285    }
1286}
1287
1288impl<D: AsRef<[u8]>> Builder for QrCodeBuilder<D> {
1289    type Output = QrCode;
1290    type Error = QrError;
1291
1292    fn build(self) -> QrResult<QrCode> {
1293        QrCodeBuilder::build(self)
1294    }
1295}
1296
1297//}}}
1298//------------------------------------------------------------------------------
1299//{{{ QrCodeStream
1300
1301/// Lazy iterator returned by [`QrCode::stream`] and
1302/// [`QrCode::stream_with_error_correction_level`].
1303#[derive(Clone, Debug)]
1304pub struct QrCodeStream<I> {
1305    inputs: I,
1306    ec_level: EcLevel,
1307}
1308
1309impl<I> Iterator for QrCodeStream<I>
1310where
1311    I: Iterator,
1312    I::Item: AsRef<[u8]>,
1313{
1314    type Item = QrResult<QrCode>;
1315
1316    fn next(&mut self) -> Option<Self::Item> {
1317        self.inputs.next().map(|input| QrCode::with_error_correction_level(input, self.ec_level))
1318    }
1319
1320    fn size_hint(&self) -> (usize, Option<usize>) {
1321        self.inputs.size_hint()
1322    }
1323}
1324
1325impl<I> FusedIterator for QrCodeStream<I>
1326where
1327    I: FusedIterator,
1328    I::Item: AsRef<[u8]>,
1329{
1330}
1331
1332impl<I> ExactSizeIterator for QrCodeStream<I>
1333where
1334    I: ExactSizeIterator,
1335    I::Item: AsRef<[u8]>,
1336{
1337    fn len(&self) -> usize {
1338        self.inputs.len()
1339    }
1340}
1341
1342//}}}
1343//------------------------------------------------------------------------------
1344//{{{ Info
1345
1346/// Metadata about a constructed [`QrCode`], returned by [`QrCode::info`].
1347///
1348/// Fields that require retaining the input data or the chosen mask (e.g.
1349/// `encoding_modes`, `mask_pattern`, `remaining_capacity`) are intentionally
1350/// omitted to keep `QrCode` zero-overhead; they may be added in a later version.
1351#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1352#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1353#[non_exhaustive]
1354pub struct Info {
1355    version: Version,
1356    ec_level: EcLevel,
1357    width: usize,
1358    module_count: usize,
1359    max_allowed_errors: usize,
1360    data_capacity_bytes: usize,
1361}
1362
1363impl Info {
1364    /// The QR [`Version`].
1365    #[must_use]
1366    pub const fn version(&self) -> Version {
1367        self.version
1368    }
1369
1370    /// The error correction level.
1371    #[must_use]
1372    pub const fn ec_level(&self) -> EcLevel {
1373        self.ec_level
1374    }
1375
1376    /// Modules per side (excluding the quiet zone).
1377    #[must_use]
1378    pub const fn width(&self) -> usize {
1379        self.width
1380    }
1381
1382    /// Total number of modules (`width * width`).
1383    #[must_use]
1384    pub const fn module_count(&self) -> usize {
1385        self.module_count
1386    }
1387
1388    /// Maximum number of erroneous modules that can still be recovered.
1389    #[must_use]
1390    pub const fn max_allowed_errors(&self) -> usize {
1391        self.max_allowed_errors
1392    }
1393
1394    /// Data capacity of this symbol in bytes.
1395    #[must_use]
1396    pub const fn data_capacity_bytes(&self) -> usize {
1397        self.data_capacity_bytes
1398    }
1399}
1400
1401//}}}
1402//------------------------------------------------------------------------------
1403//{{{ Serde (QrCodeData)
1404
1405/// A serializable view of a [`QrCode`] (matrix + metadata), enabled by the
1406/// `serde` feature. Round-trips via [`QrCode::to_serializable`] and
1407/// [`QrCode::from_serializable`].
1408#[cfg(feature = "serde")]
1409#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
1410pub struct QrCodeData {
1411    /// The [`Version`].
1412    pub version: Version,
1413    /// The error-correction level.
1414    pub ec_level: EcLevel,
1415    /// Modules per side (excluding the quiet zone).
1416    pub width: usize,
1417    /// Module colors, row-major (`width * width` entries).
1418    pub content: Vec<Color>,
1419}
1420
1421#[cfg(feature = "serde")]
1422impl QrCode {
1423    /// Serializes this QR code into a [`QrCodeData`] (requires the `serde` feature).
1424    #[must_use]
1425    pub fn to_serializable(&self) -> QrCodeData {
1426        QrCodeData { version: self.version, ec_level: self.ec_level, width: self.width, content: self.content.clone() }
1427    }
1428
1429    /// Reconstructs a [`QrCode`] from [`QrCodeData`] (requires the `serde` feature).
1430    ///
1431    /// `data` is trusted: `content.len()` must equal `width * width` (checked in
1432    /// debug builds). Pair with [`QrCode::to_serializable`].
1433    #[must_use]
1434    pub fn from_serializable(data: QrCodeData) -> Self {
1435        debug_assert_eq!(data.content.len(), data.width * data.width, "malformed QrCodeData");
1436        Self { content: data.content, version: data.version, ec_level: data.ec_level, width: data.width }
1437    }
1438}
1439
1440//}}}
1441//------------------------------------------------------------------------------
1442//{{{ Analysis
1443
1444/// Diagnostic stats for a constructed [`QrCode`], returned by [`QrCode::analyze`].
1445#[derive(Clone, Copy, Debug, PartialEq)]
1446#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1447#[non_exhaustive]
1448pub struct Analysis {
1449    dark_ratio: f64,
1450    functional_modules: usize,
1451    data_modules: usize,
1452}
1453
1454impl Analysis {
1455    /// Fraction of modules that are dark, in `0.0..=1.0`.
1456    #[must_use]
1457    pub const fn dark_ratio(&self) -> f64 {
1458        self.dark_ratio
1459    }
1460
1461    /// Number of functional modules (finder / alignment / timing / format / version).
1462    #[must_use]
1463    pub const fn functional_modules(&self) -> usize {
1464        self.functional_modules
1465    }
1466
1467    /// Number of data + error-correction modules (`width² − functional`).
1468    #[must_use]
1469    pub const fn data_modules(&self) -> usize {
1470        self.data_modules
1471    }
1472}
1473
1474//}}}
1475//------------------------------------------------------------------------------
1476//{{{ QrTemplate
1477
1478/// A reusable render-time style: dark/light hex colors, module size, and quiet
1479/// zone. Apply to a [`Renderer`] with [`Renderer::template`] when the pixel type
1480/// is a [`StyledPixel`](crate::render::StyledPixel).
1481#[derive(Clone, Debug, PartialEq, Eq)]
1482#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1483pub struct QrTemplate {
1484    /// Dark module color as a CSS hex string (e.g. `"#1a1a2e"`).
1485    pub dark_color: String,
1486    /// Light module color as a CSS hex string (e.g. `"#e0e0e0"`).
1487    pub light_color: String,
1488    /// Optional module dimensions `(width, height)` in output units/pixels.
1489    pub module_size: Option<(u32, u32)>,
1490    /// Whether to include the quiet zone.
1491    pub quiet_zone: bool,
1492}
1493
1494impl QrTemplate {
1495    /// Black on white, default size, with quiet zone — the standard look.
1496    #[must_use]
1497    pub fn minimal() -> Self {
1498        Self { dark_color: "#000000".into(), light_color: "#ffffff".into(), module_size: None, quiet_zone: true }
1499    }
1500
1501    /// Light modules on a dark background.
1502    #[must_use]
1503    pub fn dark_mode() -> Self {
1504        Self { dark_color: "#e0e0e0".into(), light_color: "#1a1a2e".into(), module_size: None, quiet_zone: true }
1505    }
1506
1507    /// Pure black/white, maximum contrast (accessibility).
1508    #[must_use]
1509    pub fn high_contrast() -> Self {
1510        Self { dark_color: "#000000".into(), light_color: "#ffffff".into(), module_size: None, quiet_zone: true }
1511    }
1512
1513    /// Corporate navy on white.
1514    #[must_use]
1515    pub fn corporate() -> Self {
1516        Self { dark_color: "#003366".into(), light_color: "#ffffff".into(), module_size: None, quiet_zone: true }
1517    }
1518}
1519
1520impl qrcode_render::RenderTemplate for QrTemplate {
1521    fn dark_color(&self) -> &str {
1522        &self.dark_color
1523    }
1524
1525    fn light_color(&self) -> &str {
1526        &self.light_color
1527    }
1528
1529    fn module_size(&self) -> Option<(u32, u32)> {
1530        self.module_size
1531    }
1532
1533    fn quiet_zone(&self) -> bool {
1534        self.quiet_zone
1535    }
1536}
1537
1538//}}}
1539//------------------------------------------------------------------------------
1540//{{{ Module iterators
1541
1542/// Iterator over the rows of a [`QrCode`], created by [`QrCode::rows`].
1543pub struct Rows<'a> {
1544    code: &'a QrCode,
1545    y: usize,
1546}
1547
1548impl<'a> Iterator for Rows<'a> {
1549    type Item = Row<'a>;
1550
1551    fn next(&mut self) -> Option<Self::Item> {
1552        let w = self.code.width;
1553        if self.y < w {
1554            let row = Row { code: self.code, y: self.y, x: 0 };
1555            self.y += 1;
1556            Some(row)
1557        } else {
1558            None
1559        }
1560    }
1561
1562    fn size_hint(&self) -> (usize, Option<usize>) {
1563        let rem = self.code.width - self.y;
1564        (rem, Some(rem))
1565    }
1566}
1567
1568impl<'a> ExactSizeIterator for Rows<'a> {
1569    fn len(&self) -> usize {
1570        self.code.width - self.y
1571    }
1572}
1573
1574impl<'a> FusedIterator for Rows<'a> {}
1575
1576/// A single row of modules, yielded by [`Rows`]. Iterates over [`Color`]s from
1577/// left to right (quiet zone excluded).
1578pub struct Row<'a> {
1579    code: &'a QrCode,
1580    y: usize,
1581    x: usize,
1582}
1583
1584impl<'a> Row<'a> {
1585    /// The number of modules in this row.
1586    #[must_use]
1587    pub fn len(&self) -> usize {
1588        self.code.width
1589    }
1590
1591    /// Whether the row is empty (always `false` for a valid QR code).
1592    #[must_use]
1593    pub fn is_empty(&self) -> bool {
1594        self.code.width == 0
1595    }
1596}
1597
1598impl<'a> Iterator for Row<'a> {
1599    type Item = Color;
1600
1601    fn next(&mut self) -> Option<Color> {
1602        let w = self.code.width;
1603        if self.x < w {
1604            let color = self.code.content[self.y * w + self.x];
1605            self.x += 1;
1606            Some(color)
1607        } else {
1608            None
1609        }
1610    }
1611
1612    fn size_hint(&self) -> (usize, Option<usize>) {
1613        let rem = self.code.width - self.x;
1614        (rem, Some(rem))
1615    }
1616}
1617
1618impl<'a> ExactSizeIterator for Row<'a> {
1619    fn len(&self) -> usize {
1620        self.code.width - self.x
1621    }
1622}
1623
1624impl<'a> FusedIterator for Row<'a> {}
1625
1626/// Iterator over the `(x, y)` coordinates of every dark module in a [`QrCode`],
1627/// created by [`QrCode::dark_modules`].
1628pub struct DarkModules<'a> {
1629    code: &'a QrCode,
1630    idx: usize,
1631}
1632
1633impl<'a> Iterator for DarkModules<'a> {
1634    type Item = (usize, usize);
1635
1636    fn next(&mut self) -> Option<(usize, usize)> {
1637        let w = self.code.width;
1638        let content = &self.code.content;
1639        while self.idx < content.len() {
1640            let i = self.idx;
1641            self.idx += 1;
1642            if content[i] == Color::Dark {
1643                return Some((i % w, i / w));
1644            }
1645        }
1646        None
1647    }
1648}
1649
1650impl<'a> FusedIterator for DarkModules<'a> {}
1651
1652//}}}
1653
1654#[cfg(test)]
1655mod tests {
1656    use crate::{EcLevel, QrCode, Version};
1657
1658    #[test]
1659    fn test_annex_i_qr() {
1660        // This uses the ISO Annex I as test vector.
1661        let code = QrCode::with_version(b"01234567", Version::Normal(1), EcLevel::M).unwrap();
1662        assert_eq!(
1663            &*code.to_debug_str('#', '.'),
1664            "\
1665             #######..#.##.#######\n\
1666             #.....#..####.#.....#\n\
1667             #.###.#.#.....#.###.#\n\
1668             #.###.#.##....#.###.#\n\
1669             #.###.#.#.###.#.###.#\n\
1670             #.....#.#...#.#.....#\n\
1671             #######.#.#.#.#######\n\
1672             ........#..##........\n\
1673             #.#####..#..#.#####..\n\
1674             ...#.#.##.#.#..#.##..\n\
1675             ..#...##.#.#.#..#####\n\
1676             ....#....#.....####..\n\
1677             ...######..#.#..#....\n\
1678             ........#.#####..##..\n\
1679             #######..##.#.##.....\n\
1680             #.....#.#.#####...#.#\n\
1681             #.###.#.#...#..#.##..\n\
1682             #.###.#.##..#..#.....\n\
1683             #.###.#.#.##.#..#.#..\n\
1684             #.....#........##.##.\n\
1685             #######.####.#..#.#.."
1686        );
1687    }
1688
1689    #[test]
1690    fn test_annex_i_micro_qr() {
1691        let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
1692        assert_eq!(
1693            &*code.to_debug_str('#', '.'),
1694            "\
1695             #######.#.#.#\n\
1696             #.....#.###.#\n\
1697             #.###.#..##.#\n\
1698             #.###.#..####\n\
1699             #.###.#.###..\n\
1700             #.....#.#...#\n\
1701             #######..####\n\
1702             .........##..\n\
1703             ##.#....#...#\n\
1704             .##.#.#.#.#.#\n\
1705             ###..#######.\n\
1706             ...#.#....##.\n\
1707             ###.#..##.###"
1708        );
1709    }
1710}
1711
1712#[cfg(test)]
1713mod api_tests {
1714    use crate::{
1715        AutoEncoder, Builder as CoreBuilder, Color, ConstVersion, ConstVersionEncoder, DynEncoder, DynRenderer,
1716        EcLevel, EncodeConfig, EncodedOutput, EncoderFactory, MicroEncoder, Mode, ModuleView, NumericMode,
1717        PluginRegistry, PostProcessor, QrCode, QrError, QrSymbol, RenderConfig, RenderOutput, RendererFactory, Version,
1718        VersionEncoder,
1719    };
1720    use alloc::{
1721        boxed::Box,
1722        string::{String, ToString},
1723        vec,
1724        vec::Vec,
1725    };
1726    use qrcode_core::traits::{
1727        Encoder as CoreEncoder, ModuleSource as CoreModuleSource, ModuleStorage as CoreModuleStorage,
1728        Renderer as CoreRenderer,
1729    };
1730
1731    fn colors(code: &QrCode) -> Vec<Color> {
1732        code.to_colors()
1733    }
1734
1735    struct TextPluginRenderer;
1736
1737    impl DynRenderer for TextPluginRenderer {
1738        fn render(&self, code: &dyn CoreModuleSource) -> Result<RenderOutput, crate::PluginError> {
1739            let mut output = String::new();
1740            for y in 0..code.height() {
1741                for x in 0..code.width() {
1742                    output.push(if code.get(x, y) == Color::Dark { '#' } else { '.' });
1743                }
1744            }
1745            Ok(RenderOutput::Text(output))
1746        }
1747    }
1748
1749    struct TextPluginFactory;
1750
1751    impl RendererFactory for TextPluginFactory {
1752        fn build(&self, _config: &RenderConfig) -> Box<dyn DynRenderer> {
1753            Box::new(TextPluginRenderer)
1754        }
1755    }
1756
1757    struct LengthPluginEncoder;
1758
1759    impl DynEncoder for LengthPluginEncoder {
1760        fn encode(&self, input: &[u8]) -> Result<EncodedOutput, crate::PluginError> {
1761            Ok(EncodedOutput::Bytes(input.len().to_string().into_bytes()))
1762        }
1763    }
1764
1765    struct LengthPluginFactory;
1766
1767    impl EncoderFactory for LengthPluginFactory {
1768        fn build(&self, _config: &EncodeConfig) -> Box<dyn DynEncoder> {
1769            Box::new(LengthPluginEncoder)
1770        }
1771    }
1772
1773    struct DarkenFirstModule;
1774
1775    impl PostProcessor for DarkenFirstModule {
1776        fn process(&self, modules: &mut dyn CoreModuleStorage) -> Result<(), crate::PluginError> {
1777            modules.set(0, 0, Color::Dark);
1778            Ok(())
1779        }
1780    }
1781
1782    #[test]
1783    fn builder_matches_with_error_correction_level() {
1784        let direct = QrCode::with_error_correction_level(b"Some data", EcLevel::H).unwrap();
1785        let built = QrCode::builder(b"Some data").ec_level(EcLevel::H).build().unwrap();
1786        assert_eq!(colors(&direct), colors(&built));
1787        assert_eq!(direct.version(), built.version());
1788        assert_eq!(direct.error_correction_level(), built.error_correction_level());
1789    }
1790
1791    #[test]
1792    fn stream_lazily_encodes_with_default_error_correction_level() {
1793        let codes = QrCode::stream(["alpha", "beta"]).collect::<Result<Vec<_>, _>>().unwrap();
1794
1795        assert_eq!(codes.iter().map(QrCode::error_correction_level).collect::<Vec<_>>(), [EcLevel::M, EcLevel::M]);
1796    }
1797
1798    #[test]
1799    fn stream_with_error_correction_level_matches_batch() {
1800        let inputs = ["alpha", "beta", "gamma"];
1801        let streamed =
1802            QrCode::stream_with_error_correction_level(inputs, EcLevel::H).collect::<Result<Vec<_>, _>>().unwrap();
1803        let batched = QrCode::batch(inputs, EcLevel::H).unwrap();
1804
1805        assert_eq!(streamed.iter().map(colors).collect::<Vec<_>>(), batched.iter().map(colors).collect::<Vec<_>>());
1806    }
1807
1808    #[test]
1809    fn stream_exposes_exact_remaining_len() {
1810        let mut stream = QrCode::stream(["alpha", "beta", "gamma"]);
1811
1812        assert_eq!(stream.len(), 3);
1813        assert!(stream.next().unwrap().is_ok());
1814        assert_eq!(stream.len(), 2);
1815    }
1816
1817    #[test]
1818    fn core_builder_trait_builds_qrcode_builder() {
1819        let code = CoreBuilder::build(QrCode::builder(b"Some data").ec_level(EcLevel::H)).unwrap();
1820
1821        assert_eq!(code.error_correction_level(), EcLevel::H);
1822    }
1823
1824    #[test]
1825    fn auto_encoder_matches_constructor() {
1826        let direct = QrCode::with_error_correction_level(b"Some data", EcLevel::H).unwrap();
1827        let encoded = AutoEncoder::new(EcLevel::H).encode(b"Some data").unwrap();
1828        assert_eq!(colors(&direct), colors(&encoded));
1829    }
1830
1831    #[test]
1832    fn micro_encoder_matches_constructor() {
1833        let direct = QrCode::micro_with_error_correction_level(b"123", EcLevel::L).unwrap();
1834        let encoded = MicroEncoder::new(EcLevel::L).encode(b"123").unwrap();
1835        assert_eq!(colors(&direct), colors(&encoded));
1836    }
1837
1838    #[test]
1839    fn version_encoder_matches_constructor() {
1840        let direct = QrCode::with_version(b"Some data", Version::Normal(1), EcLevel::M).unwrap();
1841        let encoded = VersionEncoder::new(Version::Normal(1), EcLevel::M).encode(b"Some data").unwrap();
1842        assert_eq!(colors(&direct), colors(&encoded));
1843    }
1844
1845    #[test]
1846    fn const_version_encoder_matches_dynamic_version() {
1847        const V5: Version = ConstVersion::<5>::VALUE;
1848        let direct = QrCode::with_version(b"Some data", Version::Normal(5), EcLevel::M).unwrap();
1849        let const_ctor = QrCode::with_const_version::<5, _>(b"Some data", EcLevel::M).unwrap();
1850        let encoded = ConstVersionEncoder::<5>::new(EcLevel::M).encode(b"Some data").unwrap();
1851
1852        assert_eq!(V5, Version::Normal(5));
1853        assert_eq!(ConstVersion::<5>::new().version(), Version::Normal(5));
1854        assert_eq!(ConstVersionEncoder::<5>::new(EcLevel::M).version(), Version::Normal(5));
1855        assert_eq!(colors(&direct), colors(&const_ctor));
1856        assert_eq!(colors(&direct), colors(&encoded));
1857    }
1858
1859    #[test]
1860    fn builder_matches_with_version() {
1861        let direct = QrCode::with_version(b"Some data", Version::Normal(1), EcLevel::M).unwrap();
1862        let built = QrCode::builder(b"Some data").version(Version::Normal(1)).build().unwrap();
1863        assert_eq!(colors(&direct), colors(&built));
1864    }
1865
1866    #[test]
1867    fn builder_micro_matches() {
1868        let direct = QrCode::micro_with_error_correction_level(b"123", EcLevel::L).unwrap();
1869        let built = QrCode::builder(b"123").ec_level(EcLevel::L).micro(true).build().unwrap();
1870        assert_eq!(colors(&direct), colors(&built));
1871        assert!(built.version().is_micro());
1872    }
1873
1874    #[test]
1875    fn render_with_uses_registered_renderer_and_postprocessors() {
1876        let code = QrCode::new(b"plugin").unwrap();
1877        let mut registry = PluginRegistry::new();
1878        registry.register_renderer("text", Box::new(TextPluginFactory));
1879        registry.register_postprocessor(Box::new(DarkenFirstModule));
1880
1881        let output = code.render_with(&registry, "text", &RenderConfig::new()).unwrap();
1882        let RenderOutput::Text(text) = output else {
1883            panic!("expected text output");
1884        };
1885
1886        assert_eq!(text.len(), code.width() * code.width());
1887        assert!(text.starts_with('#'));
1888    }
1889
1890    #[test]
1891    fn with_plugins_renders_through_bound_registry() {
1892        let code = QrCode::new(b"plugin").unwrap();
1893        let mut registry = PluginRegistry::new();
1894        registry.register_renderer("text", Box::new(TextPluginFactory));
1895        registry.register_postprocessor(Box::new(DarkenFirstModule));
1896
1897        let bound = code.with_plugins(&registry);
1898        let output = bound.render("text", &RenderConfig::new()).unwrap();
1899
1900        assert_eq!(bound.code().width(), code.width());
1901        assert!(bound.registry().renderer("text").is_some());
1902        assert_eq!(output, code.render_with(&registry, "text", &RenderConfig::new()).unwrap());
1903    }
1904
1905    #[test]
1906    fn render_with_reports_missing_renderer() {
1907        let code = QrCode::new(b"plugin").unwrap();
1908        let registry = PluginRegistry::new();
1909
1910        assert!(matches!(
1911            code.render_with(&registry, "missing", &RenderConfig::new()),
1912            Err(crate::PluginError::RendererNotFound(name)) if name == "missing"
1913        ));
1914    }
1915
1916    #[test]
1917    fn render_with_uses_builtin_plain_text_plugin() {
1918        let code = QrCode::new(b"plugin").unwrap();
1919        let mut registry = PluginRegistry::new();
1920        registry.register_plugin(&crate::render::plugin::PlainTextRendererPlugin);
1921        let config =
1922            RenderConfig::new().with_option("dark", "X").with_option("light", ".").with_option("quiet_zone", "0");
1923
1924        let output = code
1925            .render_with(&registry, crate::render::plugin::PlainTextRendererPlugin::RENDERER_NAME, &config)
1926            .unwrap();
1927        let expected =
1928            code.render::<char>().dark_color('X').light_color('.').quiet_zone(false).module_dimensions(1, 1).build();
1929
1930        assert_eq!(output, RenderOutput::Text(expected));
1931    }
1932
1933    #[test]
1934    fn render_with_uses_builtin_invert_modules_plugin() {
1935        let code = QrCode::new(b"plugin").unwrap();
1936        let mut registry = PluginRegistry::new();
1937        registry.register_plugin(&crate::render::plugin::PlainTextRendererPlugin);
1938        registry.register_plugin(&crate::render::plugin::InvertModulesPlugin);
1939        let config =
1940            RenderConfig::new().with_option("dark", "X").with_option("light", ".").with_option("quiet_zone", "0");
1941
1942        let output = code
1943            .render_with(&registry, crate::render::plugin::PlainTextRendererPlugin::RENDERER_NAME, &config)
1944            .unwrap();
1945        let direct =
1946            code.render::<char>().dark_color('.').light_color('X').quiet_zone(false).module_dimensions(1, 1).build();
1947
1948        assert_eq!(output, RenderOutput::Text(direct));
1949    }
1950
1951    #[test]
1952    fn encode_with_uses_registered_encoder() {
1953        let mut registry = PluginRegistry::new();
1954        registry.register_encoder("length", Box::new(LengthPluginFactory));
1955
1956        let output = QrCode::encode_with(&registry, "length", b"abcd", &EncodeConfig::new()).unwrap();
1957
1958        assert_eq!(output, EncodedOutput::Bytes(b"4".to_vec()));
1959    }
1960
1961    #[test]
1962    fn encode_with_reports_missing_encoder() {
1963        let registry = PluginRegistry::new();
1964
1965        assert!(matches!(
1966            QrCode::encode_with(&registry, "missing", b"abcd", &EncodeConfig::new()),
1967            Err(crate::PluginError::EncoderNotFound(name)) if name == "missing"
1968        ));
1969    }
1970
1971    #[test]
1972    fn builder_version_wins_over_micro() {
1973        let built = QrCode::builder(b"01234567").version(Version::Micro(2)).micro(true).build().unwrap();
1974        assert_eq!(built.version(), Version::Micro(2));
1975    }
1976
1977    #[test]
1978    fn builder_forces_byte_mode() {
1979        // Forcing Byte mode on digits must differ from the optimal (Numeric) mode.
1980        let optimal = QrCode::builder(b"01234567").version(Version::Normal(2)).build().unwrap();
1981        let byte = QrCode::builder(b"01234567").version(Version::Normal(2)).encoding_mode(Mode::Byte).build().unwrap();
1982        assert_ne!(colors(&optimal), colors(&byte));
1983    }
1984
1985    #[test]
1986    fn builder_typed_encoding_mode_matches_dynamic_mode() {
1987        let typed = QrCode::builder(b"01234567")
1988            .version(Version::Normal(2))
1989            .encoding_mode_typed::<NumericMode>()
1990            .unwrap()
1991            .build()
1992            .unwrap();
1993        let dynamic =
1994            QrCode::builder(b"01234567").version(Version::Normal(2)).encoding_mode(Mode::Numeric).build().unwrap();
1995
1996        assert_eq!(colors(&typed), colors(&dynamic));
1997    }
1998
1999    #[test]
2000    fn builder_typed_encoding_mode_rejects_invalid_input() {
2001        let err = QrCode::builder(b"12a").encoding_mode_typed::<NumericMode>().unwrap_err();
2002
2003        assert_eq!(err, QrError::InvalidCharacter { position: 2, byte: b'a' });
2004    }
2005
2006    #[test]
2007    fn rows_iterate_full_grid() {
2008        let code = QrCode::new(b"hello").unwrap();
2009        let w = code.width();
2010        let rows: Vec<Vec<Color>> = code.rows().map(|r| r.collect()).collect();
2011        assert_eq!(rows.len(), w);
2012        assert!(rows.iter().all(|r| r.len() == w));
2013        for y in 0..w {
2014            for x in 0..w {
2015                assert_eq!(rows[y][x], code[(x, y)]);
2016            }
2017        }
2018    }
2019
2020    #[test]
2021    fn rows_exact_size() {
2022        let code = QrCode::new(b"hello").unwrap();
2023        let mut rows = code.rows();
2024        let total = rows.len();
2025        let mut counted = 0;
2026        while rows.next().is_some() {
2027            counted += 1;
2028            assert_eq!(rows.len(), total - counted);
2029        }
2030    }
2031
2032    #[test]
2033    fn dark_modules_match_indexed_dark_cells() {
2034        let code = QrCode::new(b"hello").unwrap();
2035        let w = code.width();
2036        let expected: Vec<(usize, usize)> =
2037            (0..w).flat_map(|y| (0..w).map(move |x| (x, y))).filter(|&(x, y)| code[(x, y)] == Color::Dark).collect();
2038        let actual: Vec<(usize, usize)> = code.dark_modules().collect();
2039        // dark_modules scans in row-major order, matching the construction above.
2040        assert_eq!(expected, actual);
2041    }
2042
2043    #[test]
2044    fn for_url_uses_high_ec() {
2045        let code = QrCode::for_url(b"https://example.com").unwrap();
2046        assert_eq!(code.error_correction_level(), EcLevel::H);
2047    }
2048
2049    #[test]
2050    fn for_wifi_encodes_with_special_chars() {
2051        let code = QrCode::for_wifi("My;Net", "a,b", "WPA").unwrap();
2052        assert!(code.width() > 0);
2053    }
2054
2055    #[test]
2056    fn for_vcard_encodes() {
2057        let code = QrCode::for_vcard("John Doe", "+1234567890", "john@example.com").unwrap();
2058        assert!(code.width() > 0);
2059    }
2060
2061    #[test]
2062    fn for_gs1_encodes() {
2063        let code = QrCode::for_gs1("010491234512345915970331301234561842").unwrap();
2064        assert!(code.width() > 0);
2065        // GS1 uses FNC1 first position; smallest fitting version, medium EC.
2066        assert!(!code.version().is_micro());
2067        assert_eq!(code.error_correction_level(), crate::EcLevel::M);
2068    }
2069
2070    #[test]
2071    fn structured_append_encodes_n_symbols() {
2072        let codes = QrCode::structured_append(b"hello structured append world", 3, EcLevel::M).unwrap();
2073        assert_eq!(codes.len(), 3);
2074        assert!(codes.iter().all(|c| !c.version().is_micro()));
2075    }
2076
2077    #[test]
2078    fn structured_append_rejects_invalid_symbol_count() {
2079        assert_eq!(
2080            QrCode::structured_append(b"x", 1, EcLevel::M).err(),
2081            Some(crate::QrError::InvalidStructuredAppend { value: 1 })
2082        );
2083        assert_eq!(
2084            QrCode::structured_append(b"x", 17, EcLevel::M).err(),
2085            Some(crate::QrError::InvalidStructuredAppend { value: 17 })
2086        );
2087    }
2088
2089    #[test]
2090    fn info_reports_metadata() {
2091        let code = QrCode::with_version(b"01234567", Version::Normal(1), crate::EcLevel::M).unwrap();
2092        let info = code.info();
2093        assert_eq!(info.version(), Version::Normal(1));
2094        assert_eq!(info.ec_level(), crate::EcLevel::M);
2095        assert_eq!(info.width(), code.width());
2096        assert_eq!(info.module_count(), code.width() * code.width());
2097        assert!(info.data_capacity_bytes() > 0);
2098        // higher EC level => fewer data bytes for the same version
2099        let code_h = QrCode::with_version(b"01234567", Version::Normal(1), crate::EcLevel::H).unwrap();
2100        assert!(info.data_capacity_bytes() > code_h.info().data_capacity_bytes());
2101    }
2102
2103    #[test]
2104    fn colors_borrows_without_clone() {
2105        let code = QrCode::new(b"hello").unwrap();
2106        let borrowed = code.colors();
2107        assert_eq!(borrowed.len(), code.width() * code.width());
2108        // matches the cloning accessor
2109        assert_eq!(borrowed, code.to_colors().as_slice());
2110    }
2111
2112    #[test]
2113    fn module_storage_reads_and_writes_grid() {
2114        let mut code = QrCode::new(b"hello").unwrap();
2115        let width = code.width();
2116        let before = CoreModuleStorage::get(&code, 0, 0);
2117        CoreModuleStorage::set(&mut code, 0, 0, !before);
2118        assert_eq!(CoreModuleStorage::width(&code), width);
2119        assert_eq!(CoreModuleStorage::height(&code), width);
2120        assert_eq!(CoreModuleStorage::modules(&code).len(), width * width);
2121        assert_eq!(CoreModuleStorage::get(&code, 0, 0), !before);
2122    }
2123
2124    #[test]
2125    fn module_source_exposes_read_only_grid() {
2126        let code = QrCode::new(b"hello").unwrap();
2127        let width = code.width();
2128        assert_eq!(CoreModuleSource::width(&code), width);
2129        assert_eq!(CoreModuleSource::height(&code), width);
2130        assert_eq!(CoreModuleSource::modules(&code), code.colors());
2131        assert_eq!(CoreModuleSource::get(&code, 0, 0), code[(0, 0)]);
2132    }
2133
2134    #[test]
2135    fn qr_symbol_exposes_metadata() {
2136        let code = QrCode::with_version(b"hello", Version::Normal(1), EcLevel::H).unwrap();
2137
2138        assert_eq!(QrSymbol::version(&code), Version::Normal(1));
2139        assert_eq!(QrSymbol::error_correction_level(&code), EcLevel::H);
2140        assert_eq!(QrSymbol::quiet_zone(&code), 4);
2141    }
2142
2143    #[test]
2144    fn qr_symbol_uses_micro_quiet_zone() {
2145        let code = QrCode::with_version(b"123", Version::Micro(1), EcLevel::L).unwrap();
2146
2147        assert_eq!(QrSymbol::quiet_zone(&code), 2);
2148    }
2149
2150    #[test]
2151    fn module_view_exposes_borrowed_source() {
2152        let code = QrCode::new(b"hello").unwrap();
2153        let view = code.module_view();
2154
2155        assert_eq!(view.width(), code.width());
2156        assert_eq!(view.height(), code.width());
2157        assert_eq!(view.modules(), code.colors());
2158        assert_eq!(view.get(0, 0), code[(0, 0)]);
2159    }
2160
2161    #[test]
2162    fn qr_code_ref_exposes_borrowed_symbol() {
2163        let code = QrCode::with_version(b"hello", Version::Normal(1), EcLevel::H).unwrap();
2164        let borrowed = code.as_ref();
2165
2166        assert_eq!(borrowed.width(), code.width());
2167        assert_eq!(borrowed.height(), code.width());
2168        assert_eq!(borrowed.modules(), code.colors());
2169        assert_eq!(borrowed.get(0, 0), code[(0, 0)]);
2170        assert_eq!(borrowed.version(), code.version());
2171        assert_eq!(borrowed.error_correction_level(), code.error_correction_level());
2172        assert_eq!(borrowed.quiet_zone(), 4);
2173    }
2174
2175    #[test]
2176    fn render_error_is_available_from_facade() {
2177        let err = crate::render::RenderError::InvalidModuleSource { width: 3, height: 2, len: 4 };
2178
2179        assert_eq!(err.to_string(), "invalid module source dimensions: width=3, height=2, len=4");
2180    }
2181
2182    #[test]
2183    fn renderer_trait_path_matches_builder_output() {
2184        let code = QrCode::new(b"hello").unwrap();
2185        let builder_output = code.render::<char>().build();
2186        let renderer = code.render::<char>();
2187        let trait_output = CoreRenderer::render(&renderer, &code).unwrap();
2188        assert_eq!(trait_output, builder_output);
2189    }
2190
2191    #[test]
2192    fn render_builder_matches_render_output() {
2193        let code = QrCode::new(b"render builder").unwrap();
2194
2195        assert_eq!(code.render_builder::<char>().build(), code.render::<char>().build());
2196    }
2197
2198    #[cfg(feature = "async")]
2199    #[test]
2200    fn render_async_matches_render_output() {
2201        let code = QrCode::new(b"render async").unwrap();
2202        let expected = code.render::<char>().build();
2203        let runtime = tokio::runtime::Builder::new_current_thread().build().unwrap();
2204        let rendered = runtime.block_on(code.render_async::<char>()).unwrap();
2205
2206        assert_eq!(rendered, expected);
2207    }
2208
2209    #[test]
2210    fn renderer_from_symbol_matches_qrcode_render() {
2211        let code = QrCode::new(b"hello").unwrap();
2212
2213        let from_symbol = crate::render::Renderer::<char>::from_symbol(&code)
2214            .quiet_zone(false)
2215            .dark_color('X')
2216            .light_color('.')
2217            .build();
2218        let from_qrcode = code.render::<char>().quiet_zone(false).dark_color('X').light_color('.').build();
2219        assert_eq!(from_symbol, from_qrcode);
2220    }
2221
2222    #[test]
2223    fn renderer_from_borrowed_symbol_matches_owned_symbol() {
2224        let code = QrCode::with_version(b"123", Version::Micro(1), EcLevel::L).unwrap();
2225        let borrowed = code.as_ref();
2226
2227        let from_borrowed =
2228            crate::render::Renderer::<char>::from_symbol(&borrowed).dark_color('X').light_color('.').build();
2229        let from_owned = crate::render::Renderer::<char>::from_symbol(&code).dark_color('X').light_color('.').build();
2230
2231        assert_eq!(borrowed.quiet_zone(), 2);
2232        assert_eq!(from_borrowed, from_owned);
2233    }
2234
2235    #[test]
2236    fn renderer_trait_accepts_read_only_module_source() {
2237        let code = QrCode::new(b"hello").unwrap();
2238        let mut inverted = code.to_colors();
2239        for color in &mut inverted {
2240            *color = !*color;
2241        }
2242        let view = ModuleView::new(&inverted, code.width()).unwrap();
2243        let mut renderer = code.render::<char>();
2244        renderer.quiet_zone(false).dark_color('X').light_color('.');
2245
2246        let trait_output = CoreRenderer::render(&renderer, &view).unwrap();
2247        let expected = crate::render::Renderer::<char>::from_source(&view, 4)
2248            .quiet_zone(false)
2249            .dark_color('X')
2250            .light_color('.')
2251            .build();
2252        let original = code.render::<char>().quiet_zone(false).dark_color('X').light_color('.').build();
2253        assert_eq!(trait_output, expected);
2254        assert_ne!(trait_output, original);
2255    }
2256
2257    #[test]
2258    fn batch_encodes_many_and_short_circuits() {
2259        let codes = QrCode::batch(vec![b"hi"; 1000], crate::EcLevel::M).unwrap();
2260        assert_eq!(codes.len(), 1000);
2261        // short-circuit: a 5000-byte input cannot fit even v40-L.
2262        let huge: Vec<u8> = (0..5000).map(|i| (i % 256) as u8).collect();
2263        let mixed: Vec<&[u8]> = vec![&b"ok"[..], &huge[..], &b"also ok"[..]];
2264        assert!(QrCode::batch(mixed, crate::EcLevel::L).is_err());
2265    }
2266
2267    #[cfg(feature = "eps")]
2268    #[test]
2269    fn template_applies_colors() {
2270        let code = QrCode::new(b"template").unwrap();
2271        let minimal = code.render::<crate::render::eps::Color>().template(&crate::QrTemplate::minimal()).build();
2272        let dark = code.render::<crate::render::eps::Color>().template(&crate::QrTemplate::dark_mode()).build();
2273        // minimal => black foreground ("0 0 0 setrgbcolor"); dark_mode changes it.
2274        assert!(minimal.contains("0 0 0 setrgbcolor"), "minimal should use a black foreground");
2275        assert!(!dark.contains("0 0 0 setrgbcolor"), "dark_mode should change the foreground");
2276        assert_ne!(minimal, dark);
2277    }
2278
2279    #[test]
2280    fn analyze_reports_diagnostics() {
2281        let code = QrCode::with_version(b"01234567", Version::Normal(1), crate::EcLevel::M).unwrap();
2282        let a = code.analyze();
2283        let total = code.width() * code.width();
2284        assert!(a.functional_modules() > 0, "should have functional modules");
2285        assert!(a.data_modules() > 0, "should have data modules");
2286        assert_eq!(a.functional_modules() + a.data_modules(), total);
2287        assert!(a.dark_ratio() > 0.0 && a.dark_ratio() < 1.0);
2288        let dark = code.colors().iter().filter(|c| **c == Color::Dark).count();
2289        assert!((a.dark_ratio() - dark as f64 / total as f64).abs() < 1e-9);
2290    }
2291
2292    #[test]
2293    fn force_mode_without_version_auto_selects() {
2294        // Forcing Byte on digits must differ from auto (Numeric) without pinning a version.
2295        let auto = QrCode::new(b"0123456789").unwrap();
2296        let forced_byte = QrCode::builder(b"0123456789").force_mode(Mode::Byte).build().unwrap();
2297        assert_ne!(colors(&auto), colors(&forced_byte));
2298        // Forcing Numeric on digits matches auto (which also picks Numeric).
2299        let forced_num = QrCode::builder(b"0123456789").force_mode(Mode::Numeric).build().unwrap();
2300        assert_eq!(colors(&auto), colors(&forced_num));
2301        // Odd-length Kanji input surfaces InvalidCharacter via the length check.
2302        let err = QrCode::builder(b"\x93").force_mode(Mode::Kanji).build();
2303        assert!(matches!(err, Err(crate::QrError::InvalidCharacter { .. })));
2304    }
2305}
2306
2307#[cfg(all(test, feature = "image"))]
2308mod image_tests {
2309    use crate::{EcLevel, QrCode, Version};
2310    use image::{Luma, Rgb, load_from_memory};
2311
2312    #[test]
2313    fn test_annex_i_qr_as_image() {
2314        let code = QrCode::new(b"01234567").unwrap();
2315        let image = code.render::<Luma<u8>>().build();
2316        let expected =
2317            load_from_memory(include_bytes!("../docs/images/test_annex_i_qr_as_image.png")).unwrap().to_luma8();
2318        assert_eq!(image.dimensions(), expected.dimensions());
2319        assert_eq!(image.into_raw(), expected.into_raw());
2320    }
2321
2322    #[test]
2323    fn test_annex_i_micro_qr_as_image() {
2324        let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
2325        let image = code
2326            .render()
2327            .min_dimensions(200, 200)
2328            .dark_color(Rgb([128, 0, 0]))
2329            .light_color(Rgb([255, 255, 128]))
2330            .build();
2331        let expected =
2332            load_from_memory(include_bytes!("../docs/images/test_annex_i_micro_qr_as_image.png")).unwrap().to_rgb8();
2333        assert_eq!(image.dimensions(), expected.dimensions());
2334        assert_eq!(image.into_raw(), expected.into_raw());
2335    }
2336}
2337
2338#[cfg(all(test, feature = "svg"))]
2339mod svg_tests {
2340    use crate::render::svg::Color as SvgColor;
2341    use crate::{EcLevel, QrCode, Version};
2342
2343    #[test]
2344    fn test_annex_i_qr_as_svg() {
2345        let code = QrCode::new(b"01234567").unwrap();
2346        let image = code.render::<SvgColor>().build();
2347        let expected = include_str!("../docs/images/test_annex_i_qr_as_svg.svg");
2348        assert_eq!(&image, expected);
2349    }
2350
2351    #[test]
2352    fn test_annex_i_micro_qr_as_svg() {
2353        let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
2354        let image = code
2355            .render()
2356            .min_dimensions(200, 200)
2357            .dark_color(SvgColor("#800000"))
2358            .light_color(SvgColor("#ffff80"))
2359            .build();
2360        let expected = include_str!("../docs/images/test_annex_i_micro_qr_as_svg.svg");
2361        assert_eq!(&image, expected);
2362    }
2363}
2364
2365#[cfg(all(test, feature = "eps"))]
2366mod eps_tests {
2367    use crate::render::eps::Color as EpsColor;
2368    use crate::{EcLevel, QrCode, Version};
2369
2370    #[test]
2371    fn test_annex_i_qr_as_eps() {
2372        let code = QrCode::new(b"01234567").unwrap();
2373        let image = code.render::<EpsColor>().build();
2374        let expected = include_str!("../docs/images/test_annex_i_qr_as_eps.eps");
2375        assert_eq!(&image, expected);
2376    }
2377
2378    #[test]
2379    fn test_annex_i_micro_qr_as_eps() {
2380        let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
2381        let image = code
2382            .render()
2383            .min_dimensions(200, 200)
2384            .dark_color(EpsColor([0.5, 0.0, 0.0]))
2385            .light_color(EpsColor([1.0, 1.0, 0.5]))
2386            .build();
2387        let expected = include_str!("../docs/images/test_annex_i_micro_qr_as_eps.eps");
2388        assert_eq!(&image, expected);
2389    }
2390}
2391
2392#[cfg(all(test, feature = "pic"))]
2393mod pic_tests {
2394    use crate::render::pic::Color as PicColor;
2395    use crate::{EcLevel, QrCode, Version};
2396
2397    #[test]
2398    fn test_annex_i_qr_as_pic() {
2399        let code = QrCode::new(b"01234567").unwrap();
2400        let image = code.render::<PicColor>().build();
2401        let expected = include_str!("../docs/images/test_annex_i_qr_as_pic.pic");
2402        assert_eq!(&image, expected);
2403    }
2404
2405    #[test]
2406    fn test_annex_i_micro_qr_as_pic() {
2407        let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
2408        let image = code.render::<PicColor>().min_dimensions(1, 1).build();
2409        let expected = include_str!("../docs/images/test_annex_i_micro_qr_as_pic.pic");
2410        assert_eq!(&image, expected);
2411    }
2412}