use super::{ArrayCtx, ObjectCtx};
use crate::order::NodeKind;
use crate::serialization::output::Out;
pub(super) trait Style {
fn array_push_omitted(_out: &mut Out<'_>, _ctx: &ArrayCtx<'_>) {}
fn array_push_internal_gap(
_out: &mut Out<'_>,
_ctx: &ArrayCtx<'_>,
_gap: usize,
) {
}
fn object_push_omitted(_out: &mut Out<'_>, _ctx: &ObjectCtx<'_>) {}
}
fn has_any_newline(s: &str) -> bool {
s.as_bytes().contains(&b'\n') || s.as_bytes().contains(&b'\r')
}
fn maybe_push_internal_gap<S: Style>(
out: &mut Out<'_>,
ctx: &ArrayCtx<'_>,
prev_index: Option<usize>,
orig_index: usize,
) {
if let Some(prev) = prev_index {
if orig_index > prev.saturating_add(1) {
S::array_push_internal_gap(out, ctx, orig_index - prev - 1);
}
}
}
fn push_single_array_item(
out: &mut Out<'_>,
ctx: &ArrayCtx<'_>,
kind: NodeKind,
item: &str,
) {
if has_any_newline(item) {
out.push_str(item);
return;
}
match kind {
NodeKind::Array | NodeKind::Object => out.push_str(item),
_ => {
out.push_indent(ctx.depth + 1);
out.push_str(item);
}
}
}
pub(crate) fn push_array_items_with<S: Style>(
out: &mut Out<'_>,
ctx: &ArrayCtx<'_>,
) {
let mut prev_index: Option<usize> = None;
for (i, (orig_index, (kind, item))) in ctx.children.iter().enumerate() {
maybe_push_internal_gap::<S>(out, ctx, prev_index, *orig_index);
push_single_array_item(out, ctx, *kind, item);
if i + 1 < ctx.children_len {
out.push_char(',');
}
out.push_newline();
prev_index = Some(*orig_index);
}
}
fn push_value_token(out: &mut Out<'_>, v: &str) {
if v.starts_with('"') {
out.push_string_literal(v);
} else {
out.push_str(v);
}
}
pub(crate) fn push_object_items(out: &mut Out<'_>, ctx: &ObjectCtx<'_>) {
for (i, (_, (k, v))) in ctx.children.iter().enumerate() {
out.push_indent(ctx.depth + 1);
out.push_key(k);
out.push_char(':');
out.push_str(ctx.space);
push_value_token(out, v);
if i + 1 < ctx.children_len {
out.push_char(',');
}
out.push_newline();
}
}
pub(super) struct StyleNoop;
impl Style for StyleNoop {}
pub(crate) fn open_block(
out: &mut Out<'_>,
depth: usize,
inline: bool,
ch: char,
) {
if !inline {
out.push_indent(depth);
}
out.push_char(ch);
}
pub(crate) fn close_block(out: &mut Out<'_>, depth: usize, ch: char) {
out.push_indent(depth);
out.push_char(ch);
}
pub(crate) fn wrap_block(
out: &mut Out<'_>,
depth: usize,
inline: bool,
open_ch: char,
close_ch: char,
body: impl FnOnce(&mut Out<'_>),
) {
open_block(out, depth, inline, open_ch);
out.push_newline();
body(out);
close_block(out, depth, close_ch);
}