use crate::{
JsonNumber,
visitor::{JsonVisit, JsonVisitor},
};
use super::{JsonPrintOptions, Limit};
#[derive(Clone, Copy)]
pub enum JsonSize {
Expanded,
Width(usize),
}
impl JsonSize {
pub fn add(&mut self, other: Self) {
*self = match (*self, other) {
(Self::Width(a), Self::Width(b)) => Self::Width(a + b),
_ => Self::Expanded,
}
}
}
pub struct JsonSizesVisitor<'a> {
options: &'a JsonPrintOptions,
sizes: &'a mut Vec<JsonSize>,
}
impl<'a> JsonSizesVisitor<'a> {
pub fn new(options: &'a JsonPrintOptions, sizes: &'a mut Vec<JsonSize>) -> Self {
Self { options, sizes }
}
}
impl<'a> JsonVisitor for JsonSizesVisitor<'a> {
type Output = JsonSize;
fn visit_null(self) -> Self::Output {
JsonSize::Width(4)
}
fn visit_bool(self, value: bool) -> Self::Output {
if value {
JsonSize::Width(4)
} else {
JsonSize::Width(5)
}
}
fn visit_number(self, value: &JsonNumber) -> Self::Output {
JsonSize::Width(value.as_str().len())
}
fn visit_string(self, value: &str) -> Self::Output {
JsonSize::Width(printed_string_size(value))
}
fn visit_array<I: IntoIterator<Item: JsonVisit>>(self, items: I) -> Self::Output {
let index = self.sizes.len();
self.sizes.push(JsonSize::Width(0));
let mut size = JsonSize::Width(2 + self.options.object_begin + self.options.object_end);
let mut len = 0;
for (i, item) in items.into_iter().enumerate() {
if i > 0 {
size.add(JsonSize::Width(
1 + self.options.array_before_comma + self.options.array_after_comma,
));
}
let item_visitor = JsonSizesVisitor {
options: self.options,
sizes: &mut *self.sizes,
};
size.add(item.visit(item_visitor));
len += 1
}
let size = match size {
JsonSize::Expanded => JsonSize::Expanded,
JsonSize::Width(width) => match self.options.array_limit {
None => JsonSize::Width(width),
Some(Limit::Always) => JsonSize::Expanded,
Some(Limit::Item(i)) => {
if len > i {
JsonSize::Expanded
} else {
JsonSize::Width(width)
}
}
Some(Limit::ItemOrWidth(i, w)) => {
if len > i || width > w {
JsonSize::Expanded
} else {
JsonSize::Width(width)
}
}
Some(Limit::Width(w)) => {
if width > w {
JsonSize::Expanded
} else {
JsonSize::Width(width)
}
}
},
};
self.sizes[index] = size;
size
}
fn visit_object<E: IntoIterator<Item = (K, V)>, K: AsRef<str>, V: JsonVisit>(
self,
entries: E,
) -> Self::Output {
let index = self.sizes.len();
self.sizes.push(JsonSize::Width(0));
let mut size = JsonSize::Width(2 + self.options.object_begin + self.options.object_end);
let mut len = 0;
for (i, (key, value)) in entries.into_iter().enumerate() {
if i > 0 {
size.add(JsonSize::Width(
1 + self.options.object_before_comma + self.options.object_after_comma,
));
}
size.add(JsonSize::Width(
printed_string_size(key.as_ref())
+ 1 + self.options.object_before_colon
+ self.options.object_after_colon,
));
let value_visitor = JsonSizesVisitor {
options: self.options,
sizes: &mut *self.sizes,
};
size.add(value.visit(value_visitor));
len += 1;
}
let size = match size {
JsonSize::Expanded => JsonSize::Expanded,
JsonSize::Width(width) => match self.options.object_limit {
None => JsonSize::Width(width),
Some(Limit::Always) => JsonSize::Expanded,
Some(Limit::Item(i)) => {
if len > i {
JsonSize::Expanded
} else {
JsonSize::Width(width)
}
}
Some(Limit::ItemOrWidth(i, w)) => {
if len > i || width > w {
JsonSize::Expanded
} else {
JsonSize::Width(width)
}
}
Some(Limit::Width(w)) => {
if width > w {
JsonSize::Expanded
} else {
JsonSize::Width(width)
}
}
},
};
self.sizes[index] = size;
size
}
}
pub fn printed_string_size(s: &str) -> usize {
let mut width = 2;
for c in s.chars() {
width += match c {
'\\' | '\"' | '\u{0008}' | '\u{0009}' | '\u{000a}' | '\u{000c}' | '\u{000d}' => 2,
'\u{0000}'..='\u{001f}' => 6,
_ => 1,
}
}
width
}