use crate::api::{Error, Language};
use std::str::FromStr;
pub mod defaults {
pub const CHUNK_SIZE: usize = 256 * 1024;
}
#[derive(Debug, Clone)]
pub struct Config {
pub(crate) language: Language,
pub(crate) chunk_size: usize, pub(crate) threads: Option<usize>, }
impl Default for Config {
fn default() -> Self {
Self {
language: Language::default(),
chunk_size: defaults::CHUNK_SIZE,
threads: None,
}
}
}
impl Config {
pub fn builder() -> ConfigBuilder {
ConfigBuilder::default()
}
pub fn small_text() -> Self {
Self {
language: Language::default(),
chunk_size: 8 * 1024, threads: None,
}
}
pub fn large_text() -> Self {
Self {
language: Language::default(),
chunk_size: 512 * 1024, threads: None, }
}
pub fn streaming() -> Self {
Self {
language: Language::default(),
chunk_size: 32 * 1024, threads: Some(2), }
}
pub(crate) fn validate(&self) -> Result<(), Error> {
if self.chunk_size == 0 {
return Err(Error::Configuration(
"chunk_size must be greater than 0".into(),
));
}
if let Some(threads) = self.threads {
if threads == 0 {
return Err(Error::Configuration(
"threads must be greater than 0".into(),
));
}
}
Ok(())
}
}
#[derive(Debug, Default)]
pub struct ConfigBuilder {
language: Option<String>,
chunk_size: Option<usize>,
threads: Option<usize>,
}
impl ConfigBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn language(mut self, code: impl Into<String>) -> Result<Self, Error> {
self.language = Some(code.into());
Ok(self)
}
pub fn chunk_size(mut self, bytes: usize) -> Self {
self.chunk_size = Some(bytes);
self
}
pub fn threads(mut self, count: Option<usize>) -> Self {
self.threads = count;
self
}
pub fn build(self) -> Result<Config, Error> {
let mut config = Config::default();
if let Some(lang_code) = self.language {
config.language = Language::from_str(&lang_code)?;
}
if let Some(size) = self.chunk_size {
config.chunk_size = size;
}
if self.threads.is_some() {
config.threads = self.threads;
}
config.validate()?;
Ok(config)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = Config::default();
assert_eq!(config.chunk_size, defaults::CHUNK_SIZE);
assert!(config.threads.is_none());
assert!(config.validate().is_ok());
}
#[test]
fn test_config_validation() {
let config = Config {
chunk_size: 0,
..Default::default()
};
assert!(config.validate().is_err());
let config = Config {
threads: Some(0),
..Default::default()
};
assert!(config.validate().is_err());
}
#[test]
fn test_preset_configs() {
let small = Config::small_text();
assert_eq!(small.chunk_size, 8 * 1024);
assert!(small.validate().is_ok());
let large = Config::large_text();
assert_eq!(large.chunk_size, 512 * 1024);
assert!(large.validate().is_ok());
let streaming = Config::streaming();
assert_eq!(streaming.chunk_size, 32 * 1024);
assert_eq!(streaming.threads, Some(2));
assert!(streaming.validate().is_ok());
}
#[test]
fn test_config_builder_with_new_fields() {
let config = Config::builder()
.chunk_size(128 * 1024)
.threads(Some(4))
.build()
.unwrap();
assert_eq!(config.chunk_size, 128 * 1024);
assert_eq!(config.threads, Some(4));
}
#[test]
fn test_config_validation_boundary_values() {
let config = Config {
chunk_size: 1,
..Default::default()
};
assert!(config.validate().is_ok());
let config = Config {
threads: Some(1),
..Default::default()
};
assert!(config.validate().is_ok());
}
#[test]
fn test_config_builder_invalid_configurations() {
let result = Config::builder().chunk_size(0).build();
assert!(result.is_err());
let result = Config::builder().threads(Some(0)).build();
assert!(result.is_err());
}
#[test]
fn test_config_builder_with_invalid_language() {
let result = Config::builder()
.language("invalid_lang")
.unwrap() .build();
assert!(result.is_err());
match result {
Err(Error::InvalidLanguage(msg)) => {
assert!(msg.contains("invalid_lang"));
}
_ => panic!("Expected InvalidLanguage error"),
}
}
#[test]
fn test_config_validation_error_messages() {
let config = Config {
chunk_size: 0,
..Default::default()
};
match config.validate() {
Err(Error::Configuration(msg)) => {
assert_eq!(msg, "chunk_size must be greater than 0");
}
_ => panic!("Expected specific error message"),
}
let config = Config {
threads: Some(0),
..Default::default()
};
match config.validate() {
Err(Error::Configuration(msg)) => {
assert_eq!(msg, "threads must be greater than 0");
}
_ => panic!("Expected specific error message"),
}
}
#[test]
fn test_config_builder_partial_configuration() {
let config = Config::builder().chunk_size(64 * 1024).build().unwrap();
assert_eq!(config.chunk_size, 64 * 1024);
assert!(config.threads.is_none());
}
#[test]
fn test_config_builder_language_setting() {
let config = Config::builder().language("en").unwrap().build().unwrap();
assert_eq!(config.language, Language::English);
let config = Config::builder().language("ja").unwrap().build().unwrap();
assert_eq!(config.language, Language::Japanese);
}
#[test]
fn test_large_configuration_values() {
let config = Config {
chunk_size: usize::MAX / 2,
threads: Some(1024),
..Default::default()
};
assert!(config.validate().is_ok());
}
}