sakurs_core/api/
config.rs1use crate::api::{Error, Language};
4use std::str::FromStr;
5
6pub mod defaults {
8 pub const CHUNK_SIZE: usize = 256 * 1024;
10}
11
12#[derive(Debug, Clone)]
14pub struct Config {
15 pub(crate) language: Language,
16 pub(crate) chunk_size: usize, pub(crate) threads: Option<usize>, }
19
20impl Default for Config {
21 fn default() -> Self {
22 Self {
23 language: Language::default(),
24 chunk_size: defaults::CHUNK_SIZE,
25 threads: None,
26 }
27 }
28}
29
30impl Config {
31 pub fn builder() -> ConfigBuilder {
33 ConfigBuilder::default()
34 }
35
36 pub fn small_text() -> Self {
38 Self {
39 language: Language::default(),
40 chunk_size: 8 * 1024, threads: None,
42 }
43 }
44
45 pub fn large_text() -> Self {
47 Self {
48 language: Language::default(),
49 chunk_size: 512 * 1024, threads: None, }
52 }
53
54 pub fn streaming() -> Self {
56 Self {
57 language: Language::default(),
58 chunk_size: 32 * 1024, threads: Some(2), }
61 }
62
63 pub(crate) fn validate(&self) -> Result<(), Error> {
65 if self.chunk_size == 0 {
66 return Err(Error::Configuration(
67 "chunk_size must be greater than 0".into(),
68 ));
69 }
70
71 if let Some(threads) = self.threads {
72 if threads == 0 {
73 return Err(Error::Configuration(
74 "threads must be greater than 0".into(),
75 ));
76 }
77 }
78
79 Ok(())
80 }
81}
82
83#[derive(Debug, Default)]
85pub struct ConfigBuilder {
86 language: Option<String>,
87 chunk_size: Option<usize>,
88 threads: Option<usize>,
89}
90
91impl ConfigBuilder {
92 pub fn new() -> Self {
94 Self::default()
95 }
96
97 pub fn language(mut self, code: impl Into<String>) -> Result<Self, Error> {
99 self.language = Some(code.into());
100 Ok(self)
101 }
102
103 pub fn chunk_size(mut self, bytes: usize) -> Self {
105 self.chunk_size = Some(bytes);
106 self
107 }
108
109 pub fn threads(mut self, count: Option<usize>) -> Self {
111 self.threads = count;
112 self
113 }
114
115 pub fn build(self) -> Result<Config, Error> {
117 let mut config = Config::default();
118
119 if let Some(lang_code) = self.language {
120 config.language = Language::from_str(&lang_code)?;
121 }
122
123 if let Some(size) = self.chunk_size {
124 config.chunk_size = size;
125 }
126
127 if self.threads.is_some() {
128 config.threads = self.threads;
129 }
130
131 config.validate()?;
132 Ok(config)
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[test]
141 fn test_default_config() {
142 let config = Config::default();
143 assert_eq!(config.chunk_size, defaults::CHUNK_SIZE);
144 assert!(config.threads.is_none());
145 assert!(config.validate().is_ok());
146 }
147
148 #[test]
149 fn test_config_validation() {
150 let config = Config {
152 chunk_size: 0,
153 ..Default::default()
154 };
155 assert!(config.validate().is_err());
156
157 let config = Config {
159 threads: Some(0),
160 ..Default::default()
161 };
162 assert!(config.validate().is_err());
163 }
164
165 #[test]
166 fn test_preset_configs() {
167 let small = Config::small_text();
169 assert_eq!(small.chunk_size, 8 * 1024);
170 assert!(small.validate().is_ok());
171
172 let large = Config::large_text();
174 assert_eq!(large.chunk_size, 512 * 1024);
175 assert!(large.validate().is_ok());
176
177 let streaming = Config::streaming();
179 assert_eq!(streaming.chunk_size, 32 * 1024);
180 assert_eq!(streaming.threads, Some(2));
181 assert!(streaming.validate().is_ok());
182 }
183
184 #[test]
185 fn test_config_builder_with_new_fields() {
186 let config = Config::builder()
187 .chunk_size(128 * 1024)
188 .threads(Some(4))
189 .build()
190 .unwrap();
191
192 assert_eq!(config.chunk_size, 128 * 1024);
193 assert_eq!(config.threads, Some(4));
194 }
195
196 #[test]
197 fn test_config_validation_boundary_values() {
198 let config = Config {
200 chunk_size: 1,
201 ..Default::default()
202 };
203 assert!(config.validate().is_ok());
204
205 let config = Config {
207 threads: Some(1),
208 ..Default::default()
209 };
210 assert!(config.validate().is_ok());
211 }
212
213 #[test]
214 fn test_config_builder_invalid_configurations() {
215 let result = Config::builder().chunk_size(0).build();
217 assert!(result.is_err());
218
219 let result = Config::builder().threads(Some(0)).build();
221 assert!(result.is_err());
222 }
223
224 #[test]
225 fn test_config_builder_with_invalid_language() {
226 let result = Config::builder()
227 .language("invalid_lang")
228 .unwrap() .build(); assert!(result.is_err());
232 match result {
233 Err(Error::InvalidLanguage(msg)) => {
234 assert!(msg.contains("invalid_lang"));
235 }
236 _ => panic!("Expected InvalidLanguage error"),
237 }
238 }
239
240 #[test]
241 fn test_config_validation_error_messages() {
242 let config = Config {
244 chunk_size: 0,
245 ..Default::default()
246 };
247 match config.validate() {
248 Err(Error::Configuration(msg)) => {
249 assert_eq!(msg, "chunk_size must be greater than 0");
250 }
251 _ => panic!("Expected specific error message"),
252 }
253
254 let config = Config {
256 threads: Some(0),
257 ..Default::default()
258 };
259 match config.validate() {
260 Err(Error::Configuration(msg)) => {
261 assert_eq!(msg, "threads must be greater than 0");
262 }
263 _ => panic!("Expected specific error message"),
264 }
265 }
266
267 #[test]
268 fn test_config_builder_partial_configuration() {
269 let config = Config::builder().chunk_size(64 * 1024).build().unwrap();
271
272 assert_eq!(config.chunk_size, 64 * 1024);
273 assert!(config.threads.is_none());
274 }
275
276 #[test]
277 fn test_config_builder_language_setting() {
278 let config = Config::builder().language("en").unwrap().build().unwrap();
280 assert_eq!(config.language, Language::English);
281
282 let config = Config::builder().language("ja").unwrap().build().unwrap();
283 assert_eq!(config.language, Language::Japanese);
284 }
285
286 #[test]
287 fn test_large_configuration_values() {
288 let config = Config {
290 chunk_size: usize::MAX / 2,
291 threads: Some(1024),
292 ..Default::default()
293 };
294 assert!(config.validate().is_ok());
295 }
296}