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)            (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.
41//!
42//! Both renderers share one [`Layout`](render::Layout) calculation, so PNG and
43//! SVG output describe identical geometry at identical physical size.
44//!
45//! # Choosing an entropy width
46//!
47//! [`TrackingId::generate`] defaults to 32 bits of randomness — the familiar
48//! `PKG-9ED9285C` shape — which collides with ~69% probability once 100,000
49//! IDs have been issued. Production systems should configure 64 bits:
50//!
51//! ```
52//! use smart_package_tracker::{Checksum, IdGenerator};
53//!
54//! let generator = IdGenerator::builder()
55//!     .entropy_bits(64)
56//!     .checksum(Checksum::Iso7064Mod37_36)
57//!     .build()?;
58//! # Ok::<(), smart_package_tracker::Error>(())
59//! ```
60//!
61//! See [`IdGenerator`] for the full collision table.
62//!
63//! # Feature flags
64//!
65//! | Feature | Default | Effect |
66//! |---------|---------|--------|
67//! | `std` | yes | File helpers and `std::error::Error`. Without it the crate is `no_std` + `alloc`. |
68//! | `os-rng` | yes | Seed IDs from the OS CSPRNG. Turn off on targets `getrandom` does not support; `IdGenerator::generate_from_entropy` still works. |
69//! | `code128` | yes | Code 128 encoding and decoding. |
70//! | `png` | yes | PNG rendering. Implies `std`. |
71//! | `svg` | yes | SVG rendering. No extra dependencies. |
72//! | `serde` | no | `Serialize`/`Deserialize` for the public data types. |
73//!
74//! # Not yet implemented
75//!
76//! QR codes, image scanning, shipment events, and carrier integrations are
77//! deliberately absent. The [`Symbology`](symbology::Symbology) and
78//! [`Renderer`](render::Renderer) traits are the extension points for the
79//! first two; carrier integrations belong in separate crates, so that network
80//! I/O and vendor licence terms stay out of this dependency graph.
81
82// The test harness needs `std`, so only apply `no_std` outside of it.
83#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
84#![cfg_attr(docsrs, feature(doc_cfg))]
85#![forbid(unsafe_code)]
86#![warn(
87    missing_docs,
88    missing_debug_implementations,
89    rust_2018_idioms,
90    unreachable_pub
91)]
92
93extern crate alloc;
94
95pub mod error;
96pub mod id;
97pub mod render;
98pub mod symbology;
99
100mod barcode;
101
102pub use barcode::Barcode;
103pub use error::{Error, Result};
104pub use id::{Checksum, IdGenerator, IdGeneratorBuilder, TrackingId};
105pub use render::{hri_supports, Color, Length, QuietZone, RenderOptions, RenderOptionsBuilder};
106pub use symbology::{Symbol, SymbologyKind};
107
108#[cfg(feature = "code128")]
109pub use symbology::Code128;