use std::fmt;
#[cfg(feature = "contextual")]
mod contextual;
#[cfg(feature = "contextual")]
pub use self::contextual::*;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Indent {
Spaces(u8),
Tabs(u8),
}
impl Indent {
pub fn by(self, n: usize) -> IndentBy {
IndentBy(self, n)
}
}
impl fmt::Display for Indent {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Spaces(n) => {
for _ in 0..*n {
f.write_str(" ")?
}
}
Self::Tabs(n) => {
for _ in 0..*n {
f.write_str("\t")?
}
}
}
Ok(())
}
}
pub struct IndentBy(Indent, usize);
impl fmt::Display for IndentBy {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for _ in 0..self.1 {
self.0.fmt(f)?
}
Ok(())
}
}
pub struct Spaces(pub usize);
impl fmt::Display for Spaces {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for _ in 0..self.0 {
f.write_str(" ")?
}
Ok(())
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Padding {
Spaces(u8),
NewLine,
}
impl fmt::Display for Padding {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Spaces(n) => {
for _ in 0..*n {
f.write_str(" ")?
}
}
Self::NewLine => f.write_str("\n")?,
}
Ok(())
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Limit {
Always,
Item(usize),
Width(usize),
ItemOrWidth(usize, usize),
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[non_exhaustive]
pub struct Options {
pub indent: Indent,
pub array_begin: usize,
pub array_end: usize,
pub array_empty: usize,
pub array_before_comma: usize,
pub array_after_comma: usize,
pub array_limit: Option<Limit>,
pub object_begin: usize,
pub object_end: usize,
pub object_empty: usize,
pub object_before_comma: usize,
pub object_after_comma: usize,
pub object_before_colon: usize,
pub object_after_colon: usize,
pub object_limit: Option<Limit>,
}
impl Options {
#[inline(always)]
pub fn pretty() -> Self {
Self {
indent: Indent::Spaces(2),
array_begin: 1,
array_end: 1,
array_empty: 0,
array_before_comma: 0,
array_after_comma: 1,
array_limit: Some(Limit::ItemOrWidth(1, 16)),
object_begin: 1,
object_end: 1,
object_empty: 0,
object_before_comma: 0,
object_after_comma: 1,
object_before_colon: 0,
object_after_colon: 1,
object_limit: Some(Limit::ItemOrWidth(1, 16)),
}
}
#[inline(always)]
pub fn compact() -> Self {
Self {
indent: Indent::Spaces(0),
array_begin: 0,
array_end: 0,
array_empty: 0,
array_before_comma: 0,
array_after_comma: 0,
array_limit: None,
object_begin: 0,
object_end: 0,
object_empty: 0,
object_before_comma: 0,
object_after_comma: 0,
object_before_colon: 0,
object_after_colon: 0,
object_limit: None,
}
}
#[inline(always)]
pub fn inline() -> Self {
Self {
indent: Indent::Spaces(0),
array_begin: 1,
array_end: 1,
array_empty: 0,
array_before_comma: 0,
array_after_comma: 1,
array_limit: None,
object_begin: 1,
object_end: 1,
object_empty: 0,
object_before_comma: 0,
object_after_comma: 1,
object_before_colon: 0,
object_after_colon: 1,
object_limit: None,
}
}
}
#[derive(Clone, Copy)]
pub enum Size {
Expanded,
Width(usize),
}
impl Size {
pub fn add(&mut self, other: Self) {
*self = match (*self, other) {
(Self::Width(a), Self::Width(b)) => Self::Width(a + b),
_ => Self::Expanded,
}
}
}
pub trait Print {
#[inline(always)]
fn pretty_print(&self) -> Printed<'_, Self> {
self.print_with(Options::pretty())
}
#[inline(always)]
fn compact_print(&self) -> Printed<'_, Self> {
self.print_with(Options::compact())
}
#[inline(always)]
fn inline_print(&self) -> Printed<'_, Self> {
self.print_with(Options::inline())
}
#[inline(always)]
fn print_with(&self, options: Options) -> Printed<'_, Self> {
Printed(self, options, 0)
}
fn fmt_with(&self, f: &mut fmt::Formatter, options: &Options, indent: usize) -> fmt::Result;
}
impl<T: Print> Print for locspan::Stripped<T> {
fn fmt_with(&self, f: &mut fmt::Formatter, options: &Options, indent: usize) -> fmt::Result {
self.0.fmt_with(f, options, indent)
}
}
impl<T: Print, M> Print for locspan::Meta<T, M> {
fn fmt_with(&self, f: &mut fmt::Formatter, options: &Options, indent: usize) -> fmt::Result {
self.value().fmt_with(f, options, indent)
}
}
impl<'a, T: Print + ?Sized> Print for &'a T {
fn fmt_with(&self, f: &mut fmt::Formatter, options: &Options, indent: usize) -> fmt::Result {
(**self).fmt_with(f, options, indent)
}
}
pub trait PrintWithSize {
fn fmt_with_size(
&self,
f: &mut fmt::Formatter,
options: &Options,
indent: usize,
sizes: &[Size],
index: &mut usize,
) -> fmt::Result;
}
impl<T: PrintWithSize> PrintWithSize for locspan::Stripped<T> {
fn fmt_with_size(
&self,
f: &mut fmt::Formatter,
options: &Options,
indent: usize,
sizes: &[Size],
index: &mut usize,
) -> fmt::Result {
self.0.fmt_with_size(f, options, indent, sizes, index)
}
}
impl<T: PrintWithSize, M> PrintWithSize for locspan::Meta<T, M> {
fn fmt_with_size(
&self,
f: &mut fmt::Formatter,
options: &Options,
indent: usize,
sizes: &[Size],
index: &mut usize,
) -> fmt::Result {
self.value().fmt_with_size(f, options, indent, sizes, index)
}
}
impl<'a, T: PrintWithSize + ?Sized> PrintWithSize for &'a T {
fn fmt_with_size(
&self,
f: &mut fmt::Formatter,
options: &Options,
indent: usize,
sizes: &[Size],
index: &mut usize,
) -> fmt::Result {
(**self).fmt_with_size(f, options, indent, sizes, index)
}
}
pub struct Printed<'t, T: ?Sized>(&'t T, Options, usize);
impl<'t, T: Print> fmt::Display for Printed<'t, T> {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt_with(f, &self.1, self.2)
}
}
impl Print for bool {
#[inline(always)]
fn fmt_with(&self, f: &mut fmt::Formatter, _options: &Options, _indent: usize) -> fmt::Result {
if *self {
f.write_str("true")
} else {
f.write_str("false")
}
}
}
impl Print for crate::NumberBuf {
#[inline(always)]
fn fmt_with(&self, f: &mut fmt::Formatter, _options: &Options, _indent: usize) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
pub fn string_literal(s: &str, f: &mut fmt::Formatter) -> fmt::Result {
use fmt::Display;
f.write_str("\"")?;
for c in s.chars() {
match c {
'\\' => f.write_str("\\\\")?,
'\"' => f.write_str("\\\"")?,
'\u{0008}' => f.write_str("\\b")?,
'\u{0009}' => f.write_str("\\t")?,
'\u{000a}' => f.write_str("\\n")?,
'\u{000c}' => f.write_str("\\f")?,
'\u{000d}' => f.write_str("\\r")?,
'\u{0000}'..='\u{001f}' => {
f.write_str("\\u")?;
let codepoint = c as u32;
let d = codepoint & 0x000f;
let c = (codepoint & 0x00f0) >> 4;
let b = (codepoint & 0x0f00) >> 8;
let a = (codepoint & 0xf000) >> 12;
digit(a).fmt(f)?;
digit(b).fmt(f)?;
digit(c).fmt(f)?;
digit(d).fmt(f)?
}
_ => c.fmt(f)?,
}
}
f.write_str("\"")
}
fn digit(c: u32) -> char {
match c {
0x0 => '0',
0x1 => '1',
0x2 => '2',
0x3 => '3',
0x4 => '4',
0x5 => '5',
0x6 => '6',
0x7 => '7',
0x8 => '8',
0x9 => '9',
0xa => 'a',
0xb => 'b',
0xc => 'c',
0xd => 'd',
0xe => 'e',
0xf => 'f',
_ => panic!("invalid input: {}", c),
}
}
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
}
impl Print for crate::String {
#[inline(always)]
fn fmt_with(&self, f: &mut fmt::Formatter, _options: &Options, _indent: usize) -> fmt::Result {
string_literal(self, f)
}
}
pub fn print_array<I: IntoIterator>(
items: I,
f: &mut fmt::Formatter,
options: &Options,
indent: usize,
sizes: &[Size],
index: &mut usize,
) -> fmt::Result
where
I::IntoIter: ExactSizeIterator,
I::Item: PrintWithSize,
{
use fmt::Display;
let size = sizes[*index];
*index += 1;
f.write_str("[")?;
let items = items.into_iter();
if items.len() == 0 {
match size {
Size::Expanded => {
f.write_str("\n")?;
options.indent.by(indent).fmt(f)?;
}
Size::Width(_) => Spaces(options.array_empty).fmt(f)?,
}
} else {
match size {
Size::Expanded => {
f.write_str("\n")?;
for (i, item) in items.enumerate() {
if i > 0 {
Spaces(options.array_before_comma).fmt(f)?;
f.write_str(",\n")?
}
options.indent.by(indent + 1).fmt(f)?;
item.fmt_with_size(f, options, indent + 1, sizes, index)?
}
f.write_str("\n")?;
options.indent.by(indent).fmt(f)?;
}
Size::Width(_) => {
Spaces(options.array_begin).fmt(f)?;
for (i, item) in items.enumerate() {
if i > 0 {
Spaces(options.array_before_comma).fmt(f)?;
f.write_str(",")?;
Spaces(options.array_after_comma).fmt(f)?
}
item.fmt_with_size(f, options, indent + 1, sizes, index)?
}
Spaces(options.array_end).fmt(f)?
}
}
}
f.write_str("]")
}
impl<T: PrintWithSize> PrintWithSize for Vec<T> {
#[inline(always)]
fn fmt_with_size(
&self,
f: &mut fmt::Formatter,
options: &Options,
indent: usize,
sizes: &[Size],
index: &mut usize,
) -> fmt::Result {
print_array(self, f, options, indent, sizes, index)
}
}
pub fn print_object<'a, V, I: IntoIterator<Item = (&'a str, V)>>(
entries: I,
f: &mut fmt::Formatter,
options: &Options,
indent: usize,
sizes: &[Size],
index: &mut usize,
) -> fmt::Result
where
I::IntoIter: ExactSizeIterator,
V: PrintWithSize,
{
use fmt::Display;
let size = sizes[*index];
*index += 1;
f.write_str("{")?;
let entries = entries.into_iter();
if entries.len() == 0 {
match size {
Size::Expanded => {
f.write_str("\n")?;
options.indent.by(indent).fmt(f)?;
}
Size::Width(_) => Spaces(options.object_empty).fmt(f)?,
}
} else {
match size {
Size::Expanded => {
f.write_str("\n")?;
for (i, (key, value)) in entries.enumerate() {
if i > 0 {
Spaces(options.object_before_comma).fmt(f)?;
f.write_str(",\n")?
}
options.indent.by(indent + 1).fmt(f)?;
string_literal(key, f)?;
Spaces(options.object_before_colon).fmt(f)?;
f.write_str(":")?;
Spaces(options.object_after_colon).fmt(f)?;
value.fmt_with_size(f, options, indent + 1, sizes, index)?
}
f.write_str("\n")?;
options.indent.by(indent).fmt(f)?;
}
Size::Width(_) => {
Spaces(options.object_begin).fmt(f)?;
for (i, (key, value)) in entries.enumerate() {
if i > 0 {
Spaces(options.object_before_comma).fmt(f)?;
f.write_str(",")?;
Spaces(options.object_after_comma).fmt(f)?
}
string_literal(key, f)?;
Spaces(options.object_before_colon).fmt(f)?;
f.write_str(":")?;
Spaces(options.object_after_colon).fmt(f)?;
value.fmt_with_size(f, options, indent + 1, sizes, index)?
}
Spaces(options.object_end).fmt(f)?
}
}
}
f.write_str("}")
}
impl<M> PrintWithSize for crate::Object<M> {
#[inline(always)]
fn fmt_with_size(
&self,
f: &mut fmt::Formatter,
options: &Options,
indent: usize,
sizes: &[Size],
index: &mut usize,
) -> fmt::Result {
print_object(
self.iter().map(|e| (e.key.as_str(), &e.value)),
f,
options,
indent,
sizes,
index,
)
}
}
pub trait PrecomputeSize {
fn pre_compute_size(&self, options: &Options, sizes: &mut Vec<Size>) -> Size;
}
impl PrecomputeSize for bool {
#[inline(always)]
fn pre_compute_size(&self, _options: &Options, _sizes: &mut Vec<Size>) -> Size {
if *self {
Size::Width(4)
} else {
Size::Width(5)
}
}
}
impl<M> PrecomputeSize for crate::Value<M> {
fn pre_compute_size(&self, options: &Options, sizes: &mut Vec<Size>) -> Size {
match self {
crate::Value::Null => Size::Width(4),
crate::Value::Boolean(b) => b.pre_compute_size(options, sizes),
crate::Value::Number(n) => Size::Width(n.as_str().len()),
crate::Value::String(s) => Size::Width(printed_string_size(s)),
crate::Value::Array(a) => pre_compute_array_size(a, options, sizes),
crate::Value::Object(o) => pre_compute_object_size(
o.iter().map(|e| (e.key.as_str(), &e.value)),
options,
sizes,
),
}
}
}
impl<'a, T: PrecomputeSize + ?Sized> PrecomputeSize for &'a T {
fn pre_compute_size(&self, options: &Options, sizes: &mut Vec<Size>) -> Size {
(**self).pre_compute_size(options, sizes)
}
}
impl<T: PrecomputeSize> PrecomputeSize for locspan::Stripped<T> {
fn pre_compute_size(&self, options: &Options, sizes: &mut Vec<Size>) -> Size {
self.0.pre_compute_size(options, sizes)
}
}
impl<T: PrecomputeSize, M> PrecomputeSize for locspan::Meta<T, M> {
fn pre_compute_size(&self, options: &Options, sizes: &mut Vec<Size>) -> Size {
self.value().pre_compute_size(options, sizes)
}
}
pub fn pre_compute_array_size<I: IntoIterator>(
items: I,
options: &Options,
sizes: &mut Vec<Size>,
) -> Size
where
I::Item: PrecomputeSize,
{
let index = sizes.len();
sizes.push(Size::Width(0));
let mut size = Size::Width(2 + options.object_begin + options.object_end);
let mut len = 0;
for (i, item) in items.into_iter().enumerate() {
if i > 0 {
size.add(Size::Width(
1 + options.array_before_comma + options.array_after_comma,
));
}
size.add(item.pre_compute_size(options, sizes));
len += 1
}
let size = match size {
Size::Expanded => Size::Expanded,
Size::Width(width) => match options.array_limit {
None => Size::Width(width),
Some(Limit::Always) => Size::Expanded,
Some(Limit::Item(i)) => {
if len > i {
Size::Expanded
} else {
Size::Width(width)
}
}
Some(Limit::ItemOrWidth(i, w)) => {
if len > i || width > w {
Size::Expanded
} else {
Size::Width(width)
}
}
Some(Limit::Width(w)) => {
if width > w {
Size::Expanded
} else {
Size::Width(width)
}
}
},
};
sizes[index] = size;
size
}
pub fn pre_compute_object_size<'a, V, I: IntoIterator<Item = (&'a str, V)>>(
entries: I,
options: &Options,
sizes: &mut Vec<Size>,
) -> Size
where
V: PrecomputeSize,
{
let index = sizes.len();
sizes.push(Size::Width(0));
let mut size = Size::Width(2 + options.object_begin + options.object_end);
let mut len = 0;
for (i, (key, value)) in entries.into_iter().enumerate() {
if i > 0 {
size.add(Size::Width(
1 + options.object_before_comma + options.object_after_comma,
));
}
size.add(Size::Width(
printed_string_size(key) + 1 + options.object_before_colon + options.object_after_colon,
));
size.add(value.pre_compute_size(options, sizes));
len += 1;
}
let size = match size {
Size::Expanded => Size::Expanded,
Size::Width(width) => match options.object_limit {
None => Size::Width(width),
Some(Limit::Always) => Size::Expanded,
Some(Limit::Item(i)) => {
if len > i {
Size::Expanded
} else {
Size::Width(width)
}
}
Some(Limit::ItemOrWidth(i, w)) => {
if len > i || width > w {
Size::Expanded
} else {
Size::Width(width)
}
}
Some(Limit::Width(w)) => {
if width > w {
Size::Expanded
} else {
Size::Width(width)
}
}
},
};
sizes[index] = size;
size
}
impl<M> Print for crate::Value<M> {
fn fmt_with(&self, f: &mut fmt::Formatter, options: &Options, indent: usize) -> fmt::Result {
match self {
Self::Null => f.write_str("null"),
Self::Boolean(b) => b.fmt_with(f, options, indent),
Self::Number(n) => n.fmt_with(f, options, indent),
Self::String(s) => s.fmt_with(f, options, indent),
Self::Array(a) => {
let mut sizes = Vec::with_capacity(self.count(|v| v.is_array() || v.is_object()));
self.pre_compute_size(options, &mut sizes);
let mut index = 0;
a.fmt_with_size(f, options, indent, &sizes, &mut index)
}
Self::Object(o) => {
let mut sizes = Vec::with_capacity(self.count(|v| v.is_array() || v.is_object()));
self.pre_compute_size(options, &mut sizes);
let mut index = 0;
o.fmt_with_size(f, options, indent, &sizes, &mut index)
}
}
}
}
impl<M> PrintWithSize for crate::Value<M> {
fn fmt_with_size(
&self,
f: &mut fmt::Formatter,
options: &Options,
indent: usize,
sizes: &[Size],
index: &mut usize,
) -> fmt::Result {
match self {
Self::Null => f.write_str("null"),
Self::Boolean(b) => b.fmt_with(f, options, indent),
Self::Number(n) => n.fmt_with(f, options, indent),
Self::String(s) => s.fmt_with(f, options, indent),
Self::Array(a) => a.fmt_with_size(f, options, indent, sizes, index),
Self::Object(o) => o.fmt_with_size(f, options, indent, sizes, index),
}
}
}