use thiserror::Error;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum NestedSerialValue<T> {
Value(T),
Group(Vec<NestedSerialValue<T>>),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct NestingLimits {
pub max_depth: usize,
pub max_output: usize,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NestingExpansion<T> {
pub depth_reached: usize,
pub values: Vec<T>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Error)]
pub enum NestingError {
#[error("nesting depth limit must be at least 1")]
ZeroDepthLimit,
#[error("nesting output limit must be at least 1")]
ZeroOutputLimit,
#[error("nesting depth {depth} exceeds limit {max_depth}")]
DepthExceeded {
depth: usize,
max_depth: usize,
},
#[error("nesting output would exceed limit {max_output}")]
OutputExceeded {
max_output: usize,
},
}
pub fn rotate_sequence_left<T: Clone>(values: &[T], steps: usize) -> Vec<T> {
if values.is_empty() {
return Vec::new();
}
let mut rotated = values.to_vec();
let len = rotated.len();
rotated.rotate_left(steps % len);
rotated
}
pub fn expand_nested<T: Clone>(
source: &[NestedSerialValue<T>],
limits: NestingLimits,
) -> Result<NestingExpansion<T>, NestingError> {
if limits.max_depth == 0 {
return Err(NestingError::ZeroDepthLimit);
}
if limits.max_output == 0 {
return Err(NestingError::ZeroOutputLimit);
}
let mut values = Vec::new();
let mut depth_reached = 1;
expand_level(source, 1, limits, &mut depth_reached, &mut values)?;
Ok(NestingExpansion {
depth_reached,
values,
})
}
fn expand_level<T: Clone>(
source: &[NestedSerialValue<T>],
depth: usize,
limits: NestingLimits,
depth_reached: &mut usize,
values: &mut Vec<T>,
) -> Result<(), NestingError> {
if depth > limits.max_depth {
return Err(NestingError::DepthExceeded {
depth,
max_depth: limits.max_depth,
});
}
*depth_reached = (*depth_reached).max(depth);
for item in source {
match item {
NestedSerialValue::Value(value) => {
if values.len() == limits.max_output {
return Err(NestingError::OutputExceeded {
max_output: limits.max_output,
});
}
values.push(value.clone());
}
NestedSerialValue::Group(group) => {
expand_level(group, depth + 1, limits, depth_reached, values)?;
}
}
}
Ok(())
}