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> {
59 Self::with_ecl(data, Ecl::Medium)
60 }
61
62 pub fn with_ecl(data: &str, ecl: Ecl) -> Result<Self, QrError> {
69 let qr = fast_qr::QRBuilder::new(data)
70 .ecl(ecl.to_fast_qr())
71 .build()
72 .map_err(|_| QrError::DataTooLong)?;
73 Ok(Self { qr })
74 }
75
76 pub fn to_svg(&self, style: &QrStyle) -> Result<String, QrError> {
85 render::render_svg(&self.qr, style)
86 }
87
88 pub fn size(&self) -> usize {
93 self.qr.size
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn new_creates_qrcode_from_url() {
103 let qr = QrCode::new("https://example.com").unwrap();
104 assert!(qr.size() > 0);
105 }
106
107 #[test]
108 fn new_creates_qrcode_from_otpauth_uri() {
109 let uri = "otpauth://totp/Example:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Example";
110 let qr = QrCode::new(uri).unwrap();
111 assert!(qr.size() >= 21);
112 }
113
114 #[test]
115 fn new_creates_qrcode_from_empty_string() {
116 let qr = QrCode::new("");
117 assert!(qr.is_ok() || matches!(qr, Err(QrError::DataTooLong)));
118 }
119
120 #[test]
121 fn with_ecl_low() {
122 let qr = QrCode::with_ecl("test", Ecl::Low).unwrap();
123 assert!(qr.size() > 0);
124 }
125
126 #[test]
127 fn with_ecl_high() {
128 let qr = QrCode::with_ecl("test", Ecl::High).unwrap();
129 assert!(qr.size() > 0);
130 }
131
132 #[test]
133 fn higher_ecl_may_produce_larger_qr() {
134 let low = QrCode::with_ecl(
135 "Hello, World! This is some test data for QR codes.",
136 Ecl::Low,
137 )
138 .unwrap();
139 let high = QrCode::with_ecl(
140 "Hello, World! This is some test data for QR codes.",
141 Ecl::High,
142 )
143 .unwrap();
144 assert!(high.size() >= low.size());
145 }
146
147 #[test]
148 fn oversized_data_returns_data_too_long() {
149 let data = "x".repeat(8000);
150 let err = QrCode::new(&data).unwrap_err();
151 assert_eq!(err, QrError::DataTooLong);
152 }
153
154 #[test]
155 fn to_svg_produces_svg_string() {
156 let qr = QrCode::new("test").unwrap();
157 let svg = qr.to_svg(&QrStyle::default()).unwrap();
158 assert!(svg.starts_with("<svg"));
159 assert!(svg.contains("viewBox"));
160 assert!(svg.ends_with("</svg>"));
161 }
162
163 #[test]
164 fn size_returns_correct_dimension() {
165 let qr = QrCode::new("A").unwrap();
166 assert_eq!(qr.size(), 21);
167 }
168}