qr_code_styling/core/
qr_code_styling.rs1use std::fs::File;
4use std::io::Write;
5use std::path::Path;
6
7use crate::config::{QRCodeStylingBuilder, QRCodeStylingOptions};
8use crate::core::QRMatrix;
9use crate::error::Result;
10use crate::rendering::{PdfRenderer, RasterRenderer, SvgRenderer};
11use crate::types::OutputFormat;
12
13pub struct QRCodeStyling {
32 options: QRCodeStylingOptions,
33 matrix: QRMatrix,
34}
35
36impl QRCodeStylingBuilder {
37 pub fn build(self) -> Result<QRCodeStyling> {
39 let options = self.build_options()?;
40 QRCodeStyling::new(options)
41 }
42}
43
44impl QRCodeStyling {
45 pub fn builder() -> QRCodeStylingBuilder {
47 QRCodeStylingBuilder::new()
48 }
49
50 pub fn new(options: QRCodeStylingOptions) -> Result<Self> {
52 let matrix = QRMatrix::new(&options.data, &options.qr_options)?;
53
54 Ok(Self { options, matrix })
55 }
56
57 pub fn update(&mut self, data: &str) -> Result<&mut Self> {
59 self.options.data = data.to_string();
60 self.matrix = QRMatrix::new(&self.options.data, &self.options.qr_options)?;
61 Ok(self)
62 }
63
64 pub fn render_svg(&self) -> Result<String> {
66 let renderer = SvgRenderer::new(self.options.clone());
67 renderer.render(&self.matrix)
68 }
69
70 pub fn render(&self, format: OutputFormat) -> Result<Vec<u8>> {
72 match format {
73 OutputFormat::Svg => {
74 let svg = self.render_svg()?;
75 Ok(svg.into_bytes())
76 }
77 OutputFormat::Png | OutputFormat::Jpeg | OutputFormat::WebP => {
78 let svg = self.render_svg()?;
79 RasterRenderer::render(&svg, self.options.width, self.options.height, format)
80 }
81 OutputFormat::Pdf => {
82 let svg = self.render_svg()?;
84 PdfRenderer::render_from_svg(&svg, self.options.width, self.options.height)
85 }
86 }
87 }
88
89 pub fn save<P: AsRef<Path>>(&self, path: P, format: OutputFormat) -> Result<()> {
91 let data = self.render(format)?;
92 let mut file = File::create(path)?;
93 file.write_all(&data)?;
94 Ok(())
95 }
96
97 pub fn module_count(&self) -> usize {
99 self.matrix.module_count()
100 }
101
102 pub fn options(&self) -> &QRCodeStylingOptions {
104 &self.options
105 }
106
107 pub fn options_mut(&mut self) -> &mut QRCodeStylingOptions {
109 &mut self.options
110 }
111
112 pub fn regenerate(&mut self) -> Result<()> {
114 self.matrix = QRMatrix::new(&self.options.data, &self.options.qr_options)?;
115 Ok(())
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use crate::types::DotType;
123 use crate::config::DotsOptions;
124
125 #[test]
126 fn test_basic_creation() {
127 let qr = QRCodeStyling::builder()
128 .data("https://example.com")
129 .build()
130 .unwrap();
131
132 assert!(qr.module_count() >= 21);
133 }
134
135 #[test]
136 fn test_render_svg() {
137 let qr = QRCodeStyling::builder()
138 .data("Test")
139 .width(200)
140 .height(200)
141 .build()
142 .unwrap();
143
144 let svg = qr.render_svg().unwrap();
145 assert!(svg.contains("<?xml"));
146 assert!(svg.contains("<svg"));
147 assert!(svg.contains("</svg>"));
148 }
149
150 #[test]
151 fn test_update() {
152 let mut qr = QRCodeStyling::builder()
153 .data("First")
154 .build()
155 .unwrap();
156
157 let count1 = qr.module_count();
158
159 qr.update("This is a much longer string that should result in a larger QR code")
160 .unwrap();
161
162 let count2 = qr.module_count();
163
164 assert!(count2 >= count1);
166 }
167
168 #[test]
169 fn test_with_dot_options() {
170 let qr = QRCodeStyling::builder()
171 .data("Test")
172 .dots_options(DotsOptions::new(DotType::Dots))
173 .build()
174 .unwrap();
175
176 let svg = qr.render_svg().unwrap();
177 assert!(svg.contains("circle"));
178 }
179
180 #[test]
181 fn test_render_png() {
182 let qr = QRCodeStyling::builder()
183 .data("Test")
184 .width(100)
185 .height(100)
186 .build()
187 .unwrap();
188
189 let png = qr.render(OutputFormat::Png).unwrap();
190 assert_eq!(&png[0..4], &[0x89, 0x50, 0x4E, 0x47]);
192 }
193}