1use crate::qrcode::error::QrError;
2use crate::qrcode::render;
3use crate::qrcode::style::QrStyle;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum Ecl {
11 Low,
13 Medium,
15 Quartile,
17 High,
19}
20
21impl Ecl {
22 fn to_fast_qr(self) -> fast_qr::ECL {
23 match self {
24 Self::Low => fast_qr::ECL::L,
25 Self::Medium => fast_qr::ECL::M,
26 Self::Quartile => fast_qr::ECL::Q,
27 Self::High => fast_qr::ECL::H,
28 }
29 }
30}
31
32#[derive(Debug)]
48pub struct QrCode {
49 pub(super) qr: fast_qr::QRCode,
50}
51
52impl QrCode {
53 pub fn new(data: &str) -> Result<Self, QrError> {
57 Self::with_ecl(data, Ecl::Medium)
58 }
59
60 pub fn with_ecl(data: &str, ecl: Ecl) -> Result<Self, QrError> {
65 let qr = fast_qr::QRBuilder::new(data)
66 .ecl(ecl.to_fast_qr())
67 .build()
68 .map_err(|_| QrError::DataTooLong)?;
69 Ok(Self { qr })
70 }
71
72 pub fn to_svg(&self, style: &QrStyle) -> Result<String, QrError> {
78 render::render_svg(&self.qr, style)
79 }
80
81 pub fn size(&self) -> usize {
86 self.qr.size
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn new_creates_qrcode_from_url() {
96 let qr = QrCode::new("https://example.com").unwrap();
97 assert!(qr.size() > 0);
98 }
99
100 #[test]
101 fn new_creates_qrcode_from_otpauth_uri() {
102 let uri = "otpauth://totp/Example:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Example";
103 let qr = QrCode::new(uri).unwrap();
104 assert!(qr.size() >= 21);
105 }
106
107 #[test]
108 fn new_creates_qrcode_from_empty_string() {
109 let qr = QrCode::new("");
110 assert!(qr.is_ok() || matches!(qr, Err(QrError::DataTooLong)));
111 }
112
113 #[test]
114 fn with_ecl_low() {
115 let qr = QrCode::with_ecl("test", Ecl::Low).unwrap();
116 assert!(qr.size() > 0);
117 }
118
119 #[test]
120 fn with_ecl_high() {
121 let qr = QrCode::with_ecl("test", Ecl::High).unwrap();
122 assert!(qr.size() > 0);
123 }
124
125 #[test]
126 fn higher_ecl_may_produce_larger_qr() {
127 let low = QrCode::with_ecl(
128 "Hello, World! This is some test data for QR codes.",
129 Ecl::Low,
130 )
131 .unwrap();
132 let high = QrCode::with_ecl(
133 "Hello, World! This is some test data for QR codes.",
134 Ecl::High,
135 )
136 .unwrap();
137 assert!(high.size() >= low.size());
138 }
139
140 #[test]
141 fn oversized_data_returns_data_too_long() {
142 let data = "x".repeat(8000);
143 let err = QrCode::new(&data).unwrap_err();
144 assert_eq!(err, QrError::DataTooLong);
145 }
146
147 #[test]
148 fn to_svg_produces_svg_string() {
149 let qr = QrCode::new("test").unwrap();
150 let svg = qr.to_svg(&QrStyle::default()).unwrap();
151 assert!(svg.starts_with("<svg"));
152 assert!(svg.contains("viewBox"));
153 assert!(svg.ends_with("</svg>"));
154 }
155
156 #[test]
157 fn size_returns_correct_dimension() {
158 let qr = QrCode::new("A").unwrap();
159 assert_eq!(qr.size(), 21);
160 }
161}