Skip to main content

smart_package_tracker/
lib.rs

1//! Generate package tracking IDs and render them as barcodes.
2//!
3//! ```
4//! # #[cfg(all(feature = "os-rng", feature = "code128", feature = "png", feature = "svg"))]
5//! # fn main() -> Result<(), smart_package_tracker::Error> {
6//! use smart_package_tracker::{Barcode, RenderOptions, TrackingId};
7//!
8//! // 1. Mint an identifier.
9//! let id = TrackingId::generate()?;          // e.g. PKG-9ED9285C
10//!
11//! // 2. Encode it as a Code 128 barcode.
12//! let barcode = Barcode::code128(&id)?;
13//!
14//! // 3. Export it.
15//! let options = RenderOptions::default();    // 300 dpi, 13 mil, 25 mm tall
16//! let png: Vec<u8> = barcode.to_png(&options)?;
17//! let svg: String  = barcode.to_svg(&options)?;
18//!
19//! assert_eq!(&png[..8], b"\x89PNG\r\n\x1a\n");
20//! assert!(svg.contains("<svg"));
21//! # Ok(())
22//! # }
23//! # #[cfg(not(all(feature = "os-rng", feature = "code128", feature = "png", feature = "svg")))]
24//! # fn main() {}
25//! ```
26//!
27//! # Design
28//!
29//! The pipeline has two independent halves, joined by a dimension-agnostic bit
30//! grid:
31//!
32//! ```text
33//! TrackingId ──▶ Symbology::encode ──▶ Symbol ──▶ Renderer::render ──▶ bytes
34//!                (Code 128 | QR)       (BitMatrix)  (Png | Svg)
35//! ```
36//!
37//! A linear barcode is a one-row [`BitMatrix`](symbology::BitMatrix); a matrix
38//! symbology such as QR is a square one. Because renderers consume the grid
39//! rather than the symbology, adding a format means implementing
40//! [`Symbology`](symbology::Symbology) and nothing else — QR support landed
41//! without a single change to the PNG or SVG renderers.
42//!
43//! Both renderers share one [`Layout`](render::Layout) calculation, so PNG and
44//! SVG output describe identical geometry at identical physical size.
45//!
46//! Scanning runs the same pipeline backwards, meeting it at the same grid:
47//!
48//! ```text
49//! image ──▶ GrayImage ──▶ binarize ──▶ BitMatrix ──▶ Decoder::decode ──▶ payload
50//! ```
51//!
52//! ```
53//! # #[cfg(all(feature = "scan", feature = "png"))]
54//! # fn main() -> Result<(), smart_package_tracker::Error> {
55//! use smart_package_tracker::{Barcode, RenderOptions, scan};
56//!
57//! let png = Barcode::code128("PKG-9ED9285C")?.to_png(&RenderOptions::default())?;
58//! assert_eq!(scan::scan_png(&png)?.payload(), "PKG-9ED9285C");
59//! # Ok(())
60//! # }
61//! # #[cfg(not(all(feature = "scan", feature = "png")))]
62//! # fn main() {}
63//! ```
64//!
65//! See the [`scan`] module for what image conditions that covers, and what it
66//! does not.
67//!
68//! # Choosing an entropy width
69//!
70//! [`TrackingId::generate`] defaults to 32 bits of randomness — the familiar
71//! `PKG-9ED9285C` shape — which collides with ~69% probability once 100,000
72//! IDs have been issued. Production systems should configure 64 bits:
73//!
74//! ```
75//! use smart_package_tracker::{Checksum, IdGenerator};
76//!
77//! let generator = IdGenerator::builder()
78//!     .entropy_bits(64)
79//!     .checksum(Checksum::Iso7064Mod37_36)
80//!     .build()?;
81//! # Ok::<(), smart_package_tracker::Error>(())
82//! ```
83//!
84//! See [`IdGenerator`] for the full collision table.
85//!
86//! # Feature flags
87//!
88//! | Feature | Default | Effect |
89//! |---------|---------|--------|
90//! | `std` | yes | File helpers and `std::error::Error`. Without it the crate is `no_std` + `alloc`. |
91//! | `os-rng` | yes | Seed IDs from the OS CSPRNG. Turn off on targets `getrandom` does not support; `IdGenerator::generate_from_entropy` still works. |
92//! | `code128` | yes | Code 128 encoding and decoding. |
93//! | `qr` | yes | QR Code encoding. Implies `std`. |
94//! | `png` | yes | PNG rendering. Implies `std`. |
95//! | `svg` | yes | SVG rendering. No extra dependencies. |
96//! | `scan` | yes | Read Code 128 barcodes back out of images. No extra dependencies; implies `code128`. |
97//! | `serde` | no | `Serialize`/`Deserialize` for the public data types. |
98//!
99//! # Not yet implemented
100//!
101//! Shipment events and carrier integrations are deliberately absent, and
102//! belong in separate crates so that network I/O, async runtimes and vendor
103//! licence terms stay out of this dependency graph.
104//!
105//! Scanning reads linear symbologies only — there is no QR decoder here — and
106//! targets rendered labels, flatbed scans and screenshots rather than camera
107//! frames. The [`Symbology`](symbology::Symbology),
108//! [`Decoder`](symbology::Decoder) and [`Renderer`](render::Renderer) traits
109//! are the extension points for new formats.
110//!
111//! QR Code is a registered trademark of Denso Wave Incorporated.
112
113// The test harness needs `std`, so only apply `no_std` outside of it.
114#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
115#![cfg_attr(docsrs, feature(doc_cfg))]
116#![forbid(unsafe_code)]
117#![warn(
118    missing_docs,
119    missing_debug_implementations,
120    rust_2018_idioms,
121    unreachable_pub
122)]
123
124extern crate alloc;
125
126pub mod error;
127pub mod id;
128pub mod render;
129#[cfg(feature = "scan")]
130#[cfg_attr(docsrs, doc(cfg(feature = "scan")))]
131pub mod scan;
132pub mod symbology;
133
134mod barcode;
135
136pub use barcode::Barcode;
137pub use error::{Error, Result};
138pub use id::{Checksum, IdGenerator, IdGeneratorBuilder, TrackingId};
139pub use render::{hri_supports, Color, Length, QuietZone, RenderOptions, RenderOptionsBuilder};
140pub use symbology::{Symbol, SymbologyKind};
141
142#[cfg(feature = "scan")]
143pub use scan::{GrayImage, Scan, Scanner};
144
145#[cfg(feature = "code128")]
146pub use symbology::Code128;
147#[cfg(feature = "qr")]
148pub use symbology::{Ecc, Qr, QrVersion};