use indexmap::IndexMap;
use crate::compact::{compact, node_to_value};
use crate::error::DecodeError;
use crate::internal::node::Node;
use crate::options::DecodeOptions;
use crate::value::{Object, Value};
use super::accumulate::insert_value;
use super::keys::key_might_be_structured;
#[derive(Debug)]
pub(super) struct ParsedInput {
pub(super) values: FlatValues,
pub(super) has_any_structured_syntax: bool,
}
#[derive(Clone, Debug)]
pub(super) enum DefaultAccumulator {
Direct(Object),
Parsed(IndexMap<String, ParsedFlatValue>),
}
impl DefaultAccumulator {
pub(super) fn direct() -> Self {
Self::Direct(Object::new())
}
pub(super) fn direct_with_capacity(capacity: usize) -> Self {
Self::Direct(Object::with_capacity(capacity))
}
pub(super) fn ensure_parsed(&mut self) -> &mut IndexMap<String, ParsedFlatValue> {
if let Self::Direct(entries) = self {
let parsed = std::mem::take(entries)
.into_iter()
.map(|(key, value)| (key, ParsedFlatValue::concrete(value)))
.collect();
*self = Self::Parsed(parsed);
}
match self {
Self::Parsed(entries) => entries,
Self::Direct(_) => unreachable!("direct accumulator should have been promoted"),
}
}
pub(super) fn into_flat_values(self) -> FlatValues {
match self {
Self::Direct(entries) => FlatValues::Concrete(entries),
Self::Parsed(entries) => FlatValues::Parsed(entries),
}
}
}
pub(super) enum DirectInsertOutcome {
Done,
PromoteInsert {
key: String,
value: ParsedFlatValue,
via_duplicates: bool,
},
}
#[derive(Clone, Debug)]
pub(super) enum ParsedFlatValue {
Concrete(Value),
Parsed { node: Node, needs_compaction: bool },
}
impl ParsedFlatValue {
pub(super) fn concrete(value: Value) -> Self {
Self::Concrete(value)
}
pub(super) fn parsed(node: Node, needs_compaction: bool) -> Self {
Self::Parsed {
node,
needs_compaction,
}
}
pub(super) fn force_parsed(self) -> Self {
match self {
Self::Concrete(value) => Self::parsed(node_from_value(value), false),
parsed => parsed,
}
}
pub(super) fn into_node(self) -> Node {
match self {
Self::Concrete(value) => node_from_value(value),
Self::Parsed { node, .. } => node,
}
}
pub(super) fn list_length_for_combine(&self) -> usize {
match self {
Self::Concrete(Value::Array(items)) => items.len(),
Self::Concrete(value) if !value.is_empty_for_decode() => 1,
Self::Concrete(_) => 0,
Self::Parsed { node, .. } => node_list_length_for_combine(node),
}
}
}
#[derive(Clone, Debug)]
pub(super) enum FlatValues {
Concrete(Object),
Parsed(IndexMap<String, ParsedFlatValue>),
}
impl FlatValues {
pub(super) fn parsed() -> Self {
Self::Parsed(IndexMap::new())
}
pub(super) fn is_empty(&self) -> bool {
match self {
Self::Concrete(entries) => entries.is_empty(),
Self::Parsed(entries) => entries.is_empty(),
}
}
pub(super) fn key_refs(&self) -> Vec<&str> {
match self {
Self::Concrete(entries) => entries.keys().map(String::as_str).collect(),
Self::Parsed(entries) => entries.keys().map(String::as_str).collect(),
}
}
pub(super) fn get_list_length_for_combine(&self, key: &str) -> usize {
match self {
Self::Concrete(entries) => entries.get(key).map_or(0, value_list_length_for_combine),
Self::Parsed(entries) => entries
.get(key)
.map_or(0, ParsedFlatValue::list_length_for_combine),
}
}
pub(super) fn ensure_parsed(&mut self) -> &mut IndexMap<String, ParsedFlatValue> {
if let Self::Concrete(entries) = self {
let mut parsed = IndexMap::with_capacity(entries.len());
for (key, value) in std::mem::take(entries) {
parsed.insert(key, ParsedFlatValue::concrete(value));
}
*self = Self::Parsed(parsed);
}
match self {
Self::Parsed(entries) => entries,
Self::Concrete(_) => unreachable!("concrete values should have been promoted"),
}
}
pub(super) fn into_parsed_map(self) -> IndexMap<String, ParsedFlatValue> {
match self {
Self::Concrete(entries) => entries
.into_iter()
.map(|(key, value)| (key, ParsedFlatValue::concrete(value)))
.collect(),
Self::Parsed(entries) => entries,
}
}
}
pub(super) fn collect_pair_values<I>(
pairs: I,
options: &DecodeOptions,
) -> Result<ParsedInput, DecodeError>
where
I: IntoIterator<Item = (String, Value)>,
{
let mut values = FlatValues::parsed();
let mut token_count = 0usize;
let mut has_any_structured_syntax = false;
for (key, value) in pairs {
if key.is_empty() {
continue;
}
token_count += 1;
if options.throw_on_limit_exceeded && token_count > options.parameter_limit {
return Err(DecodeError::ParameterLimitExceeded {
limit: options.parameter_limit,
});
}
if !options.throw_on_limit_exceeded && token_count > options.parameter_limit {
break;
}
has_any_structured_syntax |= key_might_be_structured(&key, options);
let node_value = ParsedFlatValue::parsed(node_from_value(value), true);
let values = values.ensure_parsed();
insert_value(values.entry(key), node_value, options)?;
}
Ok(ParsedInput {
values,
has_any_structured_syntax,
})
}
pub(super) fn finalize_flat(
values: FlatValues,
options: &DecodeOptions,
) -> Result<Object, DecodeError> {
match values {
FlatValues::Concrete(values) => Ok(values),
FlatValues::Parsed(values) => {
let mut output = Object::with_capacity(values.len());
for (key, parsed_value) in values {
match parsed_value {
ParsedFlatValue::Concrete(value) => {
output.insert(key, value);
}
ParsedFlatValue::Parsed {
node,
needs_compaction,
} => {
let value = if needs_compaction {
compact(node, options.allow_sparse_lists)
} else {
node
};
match value {
Node::Undefined => continue,
Node::Value(value) => {
output.insert(key, value);
}
other => {
output.insert(key, node_to_value(other));
}
}
}
}
}
Ok(output)
}
}
}
fn node_list_length_for_combine(node: &Node) -> usize {
match node {
Node::Array(items) => items.len(),
Node::OverflowObject { max_index, .. } => max_index + 1,
Node::Value(value) if !value.is_empty_for_decode() => 1,
Node::Object(entries) if !entries.is_empty() => 1,
_ => 0,
}
}
fn node_from_value(value: Value) -> Node {
match value {
Value::Array(items) => Node::Array(items.into_iter().map(node_from_value).collect()),
Value::Object(entries) => Node::Object(
entries
.into_iter()
.map(|(key, value)| (key, node_from_value(value)))
.collect(),
),
scalar => Node::Value(scalar),
}
}
pub(super) fn value_list_length_for_combine(value: &Value) -> usize {
match value {
Value::Array(items) => items.len(),
Value::Object(entries) if !entries.is_empty() => 1,
value if !value.is_empty_for_decode() => 1,
_ => 0,
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum DefaultStorageMode {
PreferConcrete,
ForceParsed,
}