Skip to main content

qr_code_styling/config/
options.rs

1//! Main QR code styling options with builder pattern.
2
3use super::{
4    BackgroundOptions, CornersDotOptions, CornersSquareOptions, DotsOptions, ImageOptions,
5    QROptions,
6};
7use crate::error::{QRError, Result};
8use crate::types::ShapeType;
9
10/// Main configuration for QR code styling.
11#[derive(Debug, Clone, PartialEq)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13pub struct QRCodeStylingOptions {
14    /// Data to encode in the QR code.
15    pub data: String,
16    /// Width of the QR code in pixels.
17    pub width: u32,
18    /// Height of the QR code in pixels.
19    pub height: u32,
20    /// Margin around the QR code in pixels.
21    pub margin: u32,
22    /// Overall shape of the QR code.
23    pub shape: ShapeType,
24    /// Optional image/logo to embed.
25    pub image: Option<Vec<u8>>,
26    /// QR code generation options.
27    pub qr_options: QROptions,
28    /// Dot styling options.
29    pub dots_options: DotsOptions,
30    /// Corner square styling options.
31    pub corners_square_options: CornersSquareOptions,
32    /// Corner dot styling options.
33    pub corners_dot_options: CornersDotOptions,
34    /// Background styling options.
35    pub background_options: BackgroundOptions,
36    /// Image embedding options.
37    pub image_options: ImageOptions,
38}
39
40impl Default for QRCodeStylingOptions {
41    fn default() -> Self {
42        Self {
43            data: String::new(),
44            width: 300,
45            height: 300,
46            margin: 0,
47            shape: ShapeType::Square,
48            image: None,
49            qr_options: QROptions::default(),
50            dots_options: DotsOptions::default(),
51            corners_square_options: CornersSquareOptions::default(),
52            corners_dot_options: CornersDotOptions::default(),
53            background_options: BackgroundOptions::default(),
54            image_options: ImageOptions::default(),
55        }
56    }
57}
58
59/// Builder for constructing QRCodeStylingOptions.
60#[derive(Debug, Default, Clone)]
61pub struct QRCodeStylingBuilder {
62    data: Option<String>,
63    width: Option<u32>,
64    height: Option<u32>,
65    margin: Option<u32>,
66    shape: Option<ShapeType>,
67    image: Option<Vec<u8>>,
68    qr_options: Option<QROptions>,
69    dots_options: Option<DotsOptions>,
70    corners_square_options: Option<CornersSquareOptions>,
71    corners_dot_options: Option<CornersDotOptions>,
72    background_options: Option<BackgroundOptions>,
73    image_options: Option<ImageOptions>,
74}
75
76impl QRCodeStylingBuilder {
77    /// Create a new builder.
78    pub fn new() -> Self {
79        Self::default()
80    }
81
82    /// Set the data to encode.
83    pub fn data(mut self, data: impl Into<String>) -> Self {
84        self.data = Some(data.into());
85        self
86    }
87
88    /// Set the width in pixels.
89    pub fn width(mut self, width: u32) -> Self {
90        self.width = Some(width);
91        self
92    }
93
94    /// Set the height in pixels.
95    pub fn height(mut self, height: u32) -> Self {
96        self.height = Some(height);
97        self
98    }
99
100    /// Set both width and height to the same value.
101    pub fn size(mut self, size: u32) -> Self {
102        self.width = Some(size);
103        self.height = Some(size);
104        self
105    }
106
107    /// Set the margin in pixels.
108    pub fn margin(mut self, margin: u32) -> Self {
109        self.margin = Some(margin);
110        self
111    }
112
113    /// Set the overall shape.
114    pub fn shape(mut self, shape: ShapeType) -> Self {
115        self.shape = Some(shape);
116        self
117    }
118
119    /// Set the image/logo data.
120    pub fn image(mut self, image: Vec<u8>) -> Self {
121        self.image = Some(image);
122        self
123    }
124
125    /// Set QR code generation options.
126    pub fn qr_options(mut self, options: QROptions) -> Self {
127        self.qr_options = Some(options);
128        self
129    }
130
131    /// Set dot styling options.
132    pub fn dots_options(mut self, options: DotsOptions) -> Self {
133        self.dots_options = Some(options);
134        self
135    }
136
137    /// Set corner square styling options.
138    pub fn corners_square_options(mut self, options: CornersSquareOptions) -> Self {
139        self.corners_square_options = Some(options);
140        self
141    }
142
143    /// Set corner dot styling options.
144    pub fn corners_dot_options(mut self, options: CornersDotOptions) -> Self {
145        self.corners_dot_options = Some(options);
146        self
147    }
148
149    /// Set background styling options.
150    pub fn background_options(mut self, options: BackgroundOptions) -> Self {
151        self.background_options = Some(options);
152        self
153    }
154
155    /// Set image embedding options.
156    pub fn image_options(mut self, options: ImageOptions) -> Self {
157        self.image_options = Some(options);
158        self
159    }
160
161    /// Build the QRCodeStylingOptions (internal use).
162    pub(crate) fn build_options(self) -> Result<QRCodeStylingOptions> {
163        let data = self.data.ok_or(QRError::MissingData)?;
164
165        if data.is_empty() {
166            return Err(QRError::MissingData);
167        }
168
169        let width = self.width.unwrap_or(300);
170        let height = self.height.unwrap_or(300);
171
172        if width < 21 || height < 21 {
173            return Err(QRError::CanvasTooSmall { width, height });
174        }
175
176        Ok(QRCodeStylingOptions {
177            data,
178            width,
179            height,
180            margin: self.margin.unwrap_or(0),
181            shape: self.shape.unwrap_or(ShapeType::Square),
182            image: self.image,
183            qr_options: self.qr_options.unwrap_or_default(),
184            dots_options: self.dots_options.unwrap_or_default(),
185            corners_square_options: self.corners_square_options.unwrap_or_default(),
186            corners_dot_options: self.corners_dot_options.unwrap_or_default(),
187            background_options: self.background_options.unwrap_or_default(),
188            image_options: self.image_options.unwrap_or_default(),
189        })
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use super::*;
196
197    #[test]
198    fn test_builder_basic() {
199        let options = QRCodeStylingBuilder::new()
200            .data("https://example.com")
201            .width(400)
202            .height(400)
203            .build_options()
204            .unwrap();
205
206        assert_eq!(options.data, "https://example.com");
207        assert_eq!(options.width, 400);
208        assert_eq!(options.height, 400);
209    }
210
211    #[test]
212    fn test_builder_missing_data() {
213        let result = QRCodeStylingBuilder::new().build_options();
214        assert!(result.is_err());
215    }
216
217    #[test]
218    fn test_builder_empty_data() {
219        let result = QRCodeStylingBuilder::new().data("").build_options();
220        assert!(result.is_err());
221    }
222}