qr2term-rs

A stupidly simple Rust crate that renders QR codes directly in the terminal using color-inverted Unicode half-blocks.

Library
Cargo
v0.3.3
74stars
Mozilla Public License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
29/100Needs Attention
Development Activity0
Maintenance0
Community44
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
65/100Good
Architecture70
Code Quality68
Innovation55
Learning Curve65

qr2term is a small, focused Rust library for printing scannable QR codes straight to a terminal. It wraps the qrcode crate for the actual barcode encoding and crossterm for cross-platform terminal styling, and adds a rendering technique that packs two QR pixel rows into a single character cell using color-inverted ”▄” glyphs — avoiding the vertical gaps that plain block characters produce on many terminal fonts.

The public API is deliberately minimal: print_qr() writes an ANSI-colored code straight to stdout, and generate_qr_string() returns the same output as a String for custom handling. Internally, a generic Matrix<T> type handles pixel storage and quiet-zone padding, keeping the QR-generation, matrix, and rendering concerns cleanly separated across three small modules. It’s a good fit for CLI tools, setup wizards, and any terminal-only workflow that needs to hand a URL, token, or WiFi credential to a phone.

What You Get

  • Two convenience entry points - print_qr() writes an ANSI-colored QR code straight to stdout, generate_qr_string() returns the same rendering as a String for custom output handling
  • Compact terminal rendering - a technique that packs two QR code pixels into each terminal character using color-inverted half-block Unicode glyphs, avoiding the render gaps other approaches produce
  • A reusable Matrix<T> type - a generic 2D pixel grid with size() and surround() (quiet-zone padding) that can be composed into custom rendering pipelines
  • Typed error propagation - QrError re-exported from the qrcode crate so encoding failures (e.g. data too long for the QR spec) surface as Result values, not panics
  • A minimal dependency surface - built on just crossterm (styling) and qrcode (encoding), both with default features disabled to keep the footprint small

Common Use Cases

  • Printing a URL or login link as a scannable QR code directly in a CLI tool’s terminal output
  • Generating WiFi credential QR codes (the bundled example-wifi.rs builds a WIFI:S:...;T:...;P:...;; payload) for quick phone pairing
  • Piping arbitrary text or tokens from another command into a terminal QR code, as shown in the example-read.rs example
  • Embedding QR output inside larger terminal UIs or setup wizards that need to hand a value (API key, pairing code, deep link) to a mobile device

Under The Hood

Architecture The crate is a thin, cleanly layered module set. lib.rs exposes print_qr() and generate_qr_string(), both delegating to qr::Qr::from() (a wrapper around the qrcode crate’s QrCode) to produce a Matrix<Color>, then to render::Renderer to turn that matrix into terminal output. matrix.rs owns a generic square Matrix<T> with sizing and quiet-zone-padding logic, render.rs owns terminal-specific presentation (pairing rows into color-inverted half-block characters), and a small util.rs provides an integer-square-root helper shared by matrix sizing and render row-pairing math. There’s no dependency-injection layer — composition happens through plain generics (Renderer::render<W: Write>) and Default. Because qr.rs and render.rs both assume a square, row-major pixel layout from Matrix<T>, any change to that core abstraction would ripple through both.

Tech Stack Rust edition 2021 (MSRV 1.67.1), published to crates.io with docs on docs.rs. Two runtime dependencies, both with default features disabled to minimize footprint: crossterm 0.28 (cross-platform terminal styling/coloring, with the windows feature explicitly enabled) and qrcode 0.14 (QR code encoding). One dev-dependency, regex 1, used only in render.rs tests to strip ANSI escape codes when measuring rendered output width and height. CI runs on GitLab (.gitlab-ci.yml) rather than GitHub Actions — the .github directory holds only issue templates and funding metadata.

Code Quality Both matrix.rs and render.rs carry #[cfg(test)] unit tests (padding/surround behavior, size-mismatch panics, and a parametrized width/height check across several matrix sizes), and qr.rs has an oversized-input #[should_panic] test. Error handling is mostly typed and explicit — QrError propagates through Result-returning public functions via ? — though print_stdout() and generate_qr_string() call .expect() on I/O writes, treating terminal write failures as unrecoverable rather than propagated, and qr.rs carries an explicit // TODO: error handle here! acknowledging one spot that isn’t fully handled. Naming is clear and idiomatic throughout, with doc comments on all public items.

What Makes It Unique The crate’s one genuinely distinctive technical choice is documented directly in render.rs: rather than the naive approach of stacking full-block and half-block glyphs (which renders with a visible gap on many terminal fonts), it uses a single ”▄” character with inverted foreground/background coloring to represent every combination of two stacked QR pixels, eliminating the gap artifact. The author credits this rendering approach to an earlier project (qair) rather than claiming it as original. Otherwise the crate is intentionally minimal — a small, well-scoped wrapper composing qrcode and crossterm rather than reimplementing QR encoding or terminal styling itself.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search