use smallvec::SmallVec;
use std::sync::LazyLock;
pub(crate) fn parse_env_usize(name: &str, default: usize) -> usize {
std::env::var(name)
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(default)
}
pub(crate) fn parse_env_usize_opt(name: &str) -> Option<usize> {
std::env::var(name).ok().and_then(|v| v.parse().ok())
}
pub(crate) static DEFAULT_PARALLEL_THRESHOLD: LazyLock<usize> =
LazyLock::new(|| parse_env_usize("JSON_TOOLS_PARALLEL_THRESHOLD", 100));
pub(crate) static DEFAULT_NESTED_PARALLEL_THRESHOLD: LazyLock<usize> =
LazyLock::new(|| parse_env_usize("JSON_TOOLS_NESTED_PARALLEL_THRESHOLD", 100));
pub(crate) static DEFAULT_MAX_ARRAY_INDEX: LazyLock<usize> =
LazyLock::new(|| parse_env_usize("JSON_TOOLS_MAX_ARRAY_INDEX", 100_000));
pub(crate) static DEFAULT_NUM_THREADS: LazyLock<Option<usize>> =
LazyLock::new(|| parse_env_usize_opt("JSON_TOOLS_NUM_THREADS"));
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)] pub(crate) enum OperationMode {
Flatten,
Unflatten,
Normal,
}
#[derive(Debug, Clone, Default)]
pub struct FilteringConfig {
pub remove_empty_strings: bool,
pub remove_nulls: bool,
pub remove_empty_objects: bool,
pub remove_empty_arrays: bool,
}
impl FilteringConfig {
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn remove_empty_strings(mut self, enabled: bool) -> Self {
self.remove_empty_strings = enabled;
self
}
#[must_use]
pub fn remove_nulls(mut self, enabled: bool) -> Self {
self.remove_nulls = enabled;
self
}
#[must_use]
pub fn remove_empty_objects(mut self, enabled: bool) -> Self {
self.remove_empty_objects = enabled;
self
}
#[must_use]
pub fn remove_empty_arrays(mut self, enabled: bool) -> Self {
self.remove_empty_arrays = enabled;
self
}
pub fn has_any_filter(&self) -> bool {
self.remove_empty_strings
|| self.remove_nulls
|| self.remove_empty_objects
|| self.remove_empty_arrays
}
}
#[derive(Debug, Clone, Default)]
pub struct CollisionConfig {
pub handle_collisions: bool,
}
impl CollisionConfig {
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn handle_collisions(mut self, enabled: bool) -> Self {
self.handle_collisions = enabled;
self
}
pub fn has_collision_handling(&self) -> bool {
self.handle_collisions
}
}
#[derive(Debug, Clone, Default)]
pub struct ReplacementConfig {
pub key_replacements: SmallVec<[(String, String); 2]>,
pub value_replacements: SmallVec<[(String, String); 2]>,
}
impl ReplacementConfig {
pub fn new() -> Self {
Self {
key_replacements: SmallVec::new(),
value_replacements: SmallVec::new(),
}
}
#[must_use]
pub fn add_key_replacement(
mut self,
find: impl Into<String>,
replace: impl Into<String>,
) -> Self {
self.key_replacements.push((find.into(), replace.into()));
self
}
#[must_use]
pub fn add_value_replacement(
mut self,
find: impl Into<String>,
replace: impl Into<String>,
) -> Self {
self.value_replacements.push((find.into(), replace.into()));
self
}
pub fn has_key_replacements(&self) -> bool {
!self.key_replacements.is_empty()
}
pub fn has_value_replacements(&self) -> bool {
!self.value_replacements.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct ProcessingConfig {
pub separator: String,
pub lowercase_keys: bool,
pub filtering: FilteringConfig,
pub collision: CollisionConfig,
pub replacements: ReplacementConfig,
pub auto_convert_types: bool,
pub parallel_threshold: usize,
pub num_threads: Option<usize>,
pub nested_parallel_threshold: usize,
pub max_array_index: usize,
}
impl Default for ProcessingConfig {
fn default() -> Self {
Self {
separator: ".".to_string(),
lowercase_keys: false,
filtering: FilteringConfig::default(),
collision: CollisionConfig::default(),
replacements: ReplacementConfig::default(),
auto_convert_types: false,
parallel_threshold: *DEFAULT_PARALLEL_THRESHOLD,
num_threads: None, nested_parallel_threshold: *DEFAULT_NESTED_PARALLEL_THRESHOLD,
max_array_index: *DEFAULT_MAX_ARRAY_INDEX,
}
}
}
impl ProcessingConfig {
pub fn new() -> Self {
Self::default()
}
pub(crate) fn effective_thread_count(&self, item_count: usize) -> usize {
let base = self.num_threads.unwrap_or_else(|| {
std::thread::available_parallelism()
.map(|p| p.get())
.unwrap_or(4)
});
base.max(1).min(item_count)
}
#[must_use]
pub fn separator(mut self, separator: impl Into<String>) -> Self {
self.separator = separator.into();
self
}
#[must_use]
pub fn lowercase_keys(mut self, enabled: bool) -> Self {
self.lowercase_keys = enabled;
self
}
#[must_use]
pub fn filtering(mut self, filtering: FilteringConfig) -> Self {
self.filtering = filtering;
self
}
#[must_use]
pub fn collision(mut self, collision: CollisionConfig) -> Self {
self.collision = collision;
self
}
#[must_use]
pub fn replacements(mut self, replacements: ReplacementConfig) -> Self {
self.replacements = replacements;
self
}
}