Skip to content

Commit

Permalink
Improved Accessor Formatting
Browse files Browse the repository at this point in the history
Added #[must_use] Attributes to DIsplay Wrappers
Reduced Allocations in Indentation Formatting
  • Loading branch information
Redfire75369 committed Jan 16, 2024
1 parent 7572f0d commit 403dd73
Show file tree
Hide file tree
Showing 18 changed files with 141 additions and 60 deletions.
15 changes: 8 additions & 7 deletions ion/src/format/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ use std::fmt::{Display, Formatter, Write};
use colored::Colorize;

use crate::{Array, Context};
use crate::format::{INDENT, NEWLINE};
use crate::format::{indent_str, NEWLINE};
use crate::format::Config;
use crate::format::descriptor::format_descriptor;
use crate::format::object::write_remaining;

/// Formats an [JavaScript Array](Array) as a string using the given [configuration](Config).
/// Formats an [JavaScript Array](Array) using the given [configuration](Config).
pub fn format_array<'cx>(cx: &'cx Context, cfg: Config, array: &'cx Array<'cx>) -> ArrayDisplay<'cx> {
ArrayDisplay { cx, array, cfg }
}

#[must_use]
pub struct ArrayDisplay<'cx> {
cx: &'cx Context,
array: &'cx Array<'cx>,
Expand All @@ -41,12 +42,12 @@ impl Display for ArrayDisplay<'_> {
f.write_str(NEWLINE)?;
let len = length.clamp(0, 100);

let inner = INDENT.repeat((self.cfg.indentation + self.cfg.depth + 1) as usize);
let inner = indent_str((self.cfg.indentation + self.cfg.depth + 1) as usize);

for index in 0..len {
f.write_str(&inner)?;
inner.fmt(f)?;
let desc = self.array.get_descriptor(self.cx, index).unwrap();
format_descriptor(self.cx, self.cfg, &desc).fmt(f)?;
format_descriptor(self.cx, self.cfg, &desc, Some(self.array.as_object())).fmt(f)?;
",".color(colour).fmt(f)?;
f.write_str(NEWLINE)?;
}
Expand All @@ -58,7 +59,7 @@ impl Display for ArrayDisplay<'_> {

for index in 0..len {
let desc = self.array.get_descriptor(self.cx, index).unwrap();
format_descriptor(self.cx, self.cfg, &desc).fmt(f)?;
format_descriptor(self.cx, self.cfg, &desc, Some(self.array.as_object())).fmt(f)?;

if index != len - 1 {
",".color(colour).fmt(f)?;
Expand All @@ -72,7 +73,7 @@ impl Display for ArrayDisplay<'_> {
write_remaining(f, remaining as usize, inner.as_deref(), colour)?;

if self.cfg.multiline {
f.write_str(&INDENT.repeat((self.cfg.indentation + self.cfg.depth) as usize))?;
indent_str((self.cfg.indentation + self.cfg.depth) as usize).fmt(f)?;
}

"]".color(colour).fmt(f)
Expand Down
3 changes: 2 additions & 1 deletion ion/src/format/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ use crate::{Context, Object};
use crate::format::Config;
use crate::format::primitive::format_primitive;

/// Formats a boxed primitive ([Object]) as a string using the given [configuration](Config).
/// Formats a boxed primitive ([Object]) using the given [configuration](Config).
/// The supported boxed types are `Boolean`, `Number`, `String` and `BigInt`.
pub fn format_boxed<'cx>(cx: &'cx Context, cfg: Config, object: &'cx Object<'cx>) -> BoxedDisplay<'cx> {
BoxedDisplay { cx, object, cfg }
}

#[must_use]
pub struct BoxedDisplay<'cx> {
cx: &'cx Context,
object: &'cx Object<'cx>,
Expand Down
3 changes: 2 additions & 1 deletion ion/src/format/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ use crate::{Context, Object};
use crate::format::Config;
use crate::format::object::format_plain_object;

/// Formats a [JavaScript Object](Object), along with the name of its constructor, as a string with the given [configuration](Config).
/// Formats a [JavaScript Object](Object), along with the name of its constructor, with the given [configuration](Config).
pub fn format_class_object<'cx>(cx: &'cx Context, cfg: Config, object: &'cx Object<'cx>) -> ClassObjectDisplay<'cx> {
ClassObjectDisplay { cx, object, cfg }
}

#[must_use]
pub struct ClassObjectDisplay<'cx> {
cx: &'cx Context,
object: &'cx Object<'cx>,
Expand Down
1 change: 1 addition & 0 deletions ion/src/format/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ impl ColourConfig {

/// Represents configuration for formatting
#[derive(Clone, Copy, Debug)]
#[must_use]
pub struct Config {
pub colours: ColourConfig,
pub iteration: IteratorFlags,
Expand Down
3 changes: 2 additions & 1 deletion ion/src/format/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ use colored::Colorize;
use crate::{Context, Date};
use crate::format::Config;

/// Formats a [JavaScript Date](Date) as a string using the given [configuration](Config).
/// Formats a [JavaScript Date](Date) using the given [configuration](Config).
pub fn format_date<'cx>(cx: &'cx Context, cfg: Config, date: &'cx Date<'cx>) -> DateDisplay<'cx> {
DateDisplay { cx, date, cfg }
}

#[must_use]
pub struct DateDisplay<'cx> {
cx: &'cx Context,
date: &'cx Date<'cx>,
Expand Down
64 changes: 54 additions & 10 deletions ion/src/format/descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,76 @@ use std::fmt::{Display, Formatter};

use colored::Colorize;

use crate::{Context, PropertyDescriptor};
use crate::{Context, Object, PropertyDescriptor};
use crate::format::{Config, format_value};
use crate::format::object::format_object;
use crate::format::primitive::format_primitive;

/// Formats a [descriptor](PropertyDescriptor) with the given [configuration](Config).
pub fn format_descriptor<'cx>(
cx: &'cx Context, cfg: Config, desc: &'cx PropertyDescriptor<'cx>,
cx: &'cx Context, cfg: Config, desc: &'cx PropertyDescriptor<'cx>, object: Option<&'cx Object<'cx>>,
) -> DescriptorDisplay<'cx> {
DescriptorDisplay { cx, cfg, desc }
DescriptorDisplay { cx, cfg, desc, object }
}

#[must_use]
pub struct DescriptorDisplay<'cx> {
cx: &'cx Context,
cfg: Config,
desc: &'cx PropertyDescriptor<'cx>,
object: Option<&'cx Object<'cx>>,
}

impl Display for DescriptorDisplay<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match (self.desc.getter(self.cx), self.desc.setter(self.cx)) {
(Some(_), Some(_)) => "[Getter/Setter]".color(self.cfg.colours.function).fmt(f),
(Some(_), None) => "[Getter]".color(self.cfg.colours.function).fmt(f),
(None, Some(_)) => "[Setter]".color(self.cfg.colours.function).fmt(f),
(None, None) => match self.desc.value(self.cx) {
let color = self.cfg.colours.function;

if let Some(getter) = self.desc.getter(self.cx) {
"[Getter".color(color).fmt(f)?;
if self.desc.setter(self.cx).is_some() {
"/Setter".color(color).fmt(f)?;
}
return if let Some(object) = self.object {
let value = match getter.call(self.cx, object, &[]) {
Ok(value) => value,
Err(report) => {
f.write_str("<inspection threw ")?;
match report {
Some(mut report) => {
report.stack = None;
report.format(self.cx).fmt(f)?;
}
None => f.write_str("unknown error")?,
}
f.write_str(">")?;
return "]".color(color).fmt(f);
}
};
if value.handle().is_object() {
"] ".color(color).fmt(f)?;
format_object(
self.cx,
self.cfg.depth(self.cfg.depth + 1).quoted(true),
value.to_object(self.cx),
)
.fmt(f)
} else {
": ".color(color).fmt(f)?;
format_primitive(self.cx, self.cfg.quoted(true), &value).fmt(f)?;
"]".color(color).fmt(f)
}
} else {
"]".color(color).fmt(f)
};
}

if self.desc.setter(self.cx).is_some() {
"[Setter]".color(color).fmt(f)
} else {
match self.desc.value(self.cx) {
Some(value) => format_value(self.cx, self.cfg.depth(self.cfg.depth + 1).quoted(true), &value).fmt(f),
None => unreachable!(),
},
None => f.write_str("<empty descriptor>"),
}
}
}
}
3 changes: 2 additions & 1 deletion ion/src/format/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use indent::indent_by;
use crate::{Context, Function};
use crate::format::Config;

/// Formats a [JavaScript Function](Function) as a string, using the given [configuration](Config).
/// Formats a [function](Function), using the given [configuration](Config).
///
/// ### Format
/// ```js
Expand All @@ -25,6 +25,7 @@ pub fn format_function<'cx>(cx: &'cx Context, cfg: Config, function: &'cx Functi
FunctionDisplay { cx, function, cfg }
}

#[must_use]
pub struct FunctionDisplay<'cx> {
cx: &'cx Context,
function: &'cx Function<'cx>,
Expand Down
1 change: 1 addition & 0 deletions ion/src/format/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub fn format_key<'cx>(cx: &'cx Context, cfg: Config, key: &'cx OwnedKey<'cx>) -
KeyDisplay { cx, cfg, key }
}

#[must_use]
pub struct KeyDisplay<'cx> {
cx: &'cx Context,
cfg: Config,
Expand Down
20 changes: 19 additions & 1 deletion ion/src/format/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

use std::borrow::Cow;
use std::fmt;
use std::fmt::{Display, Formatter};
use std::str;

pub use config::Config;

Expand All @@ -31,11 +33,27 @@ pub mod typedarray;
pub const INDENT: &str = " ";
pub const NEWLINE: &str = "\n";

/// Formats a [JavaScript Value](Value) as a string with the given [configuration](Config).
#[must_use]
pub fn indent_str(indentation: usize) -> Cow<'static, str> {
const MAX_INDENTS: usize = 128;
static INDENTS: &str = match str::from_utf8(&[b' '; MAX_INDENTS * 2]) {
Ok(indents) => indents,
_ => unreachable!(),
};

if indentation <= 128 {
Cow::Borrowed(&INDENTS[0..indentation * INDENT.len()])
} else {
Cow::Owned(INDENT.repeat(indentation))
}
}

/// Formats a [JavaScript Value](Value) with the given [configuration](Config).
pub fn format_value<'cx>(cx: &'cx Context, cfg: Config, value: &'cx Value<'cx>) -> ValueDisplay<'cx> {
ValueDisplay { cx, value, cfg }
}

#[must_use]
pub struct ValueDisplay<'cx> {
cx: &'cx Context,
value: &'cx Value<'cx>,
Expand Down
31 changes: 15 additions & 16 deletions ion/src/format/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use mozjs::jsapi::{ESClass, Type};

use crate::{Array, Context, Date, Exception, Function, Object, Promise, PropertyDescriptor, PropertyKey, RegExp};
use crate::conversions::ToValue;
use crate::format::{INDENT, NEWLINE};
use crate::format::{indent_str, NEWLINE};
use crate::format::array::format_array;
use crate::format::boxed::format_boxed;
use crate::format::class::format_class_object;
Expand All @@ -31,12 +31,13 @@ use crate::typedarray::{
Uint16Array, Uint32Array, Uint8Array,
};

/// Formats a [JavaScript Object](Object), depending on its class, as a string using the given [configuration](Config).
/// Formats a [JavaScript Object](Object), depending on its class, using the given [configuration](Config).
/// The object is passed to more specific formatting functions, such as [format_array] and [format_date].
pub fn format_object<'cx>(cx: &'cx Context, cfg: Config, object: Object<'cx>) -> ObjectDisplay<'cx> {
ObjectDisplay { cx, object, cfg }
}

#[must_use]
pub struct ObjectDisplay<'cx> {
cx: &'cx Context,
object: Object<'cx>,
Expand All @@ -62,10 +63,10 @@ impl Display for ObjectDisplay<'_> {
ESC::Function => format_function(cx, cfg, &Function::from_object(cx, &self.object).unwrap()).fmt(f),
ESC::ArrayBuffer => format_array_buffer(cfg, &ArrayBuffer::from(object.into_local()).unwrap()).fmt(f),
ESC::Error => match Exception::from_object(cx, &self.object) {
Exception::Error(error) => f.write_str(&error.format()),
Exception::Error(error) => error.format().fmt(f),
_ => unreachable!("Expected Error"),
},
ESC::Object => format_plain_object(cx, cfg, &Object::from(object.into_local())).fmt(f),
ESC::Object => format_plain_object(cx, cfg, &self.object).fmt(f),
ESC::Other => {
if let Some(view) = ArrayBufferView::from(cx.root_object(object.handle().get())) {
'view: {
Expand Down Expand Up @@ -102,20 +103,18 @@ impl Display for ObjectDisplay<'_> {

format_class_object(cx, cfg, &self.object).fmt(f)
}
_ => {
let source = self.object.as_value(cx).to_source(cx).to_owned(cx);
f.write_str(&source)
}
_ => self.object.as_value(cx).to_source(cx).to_owned(cx).fmt(f),
}
}
}

/// Formats a [JavaScript Object](Object) as a string using the given [configuration](Config).
/// Formats a [JavaScript Object](Object) using the given [configuration](Config).
/// Disregards the class of the object.
pub fn format_plain_object<'cx>(cx: &'cx Context, cfg: Config, object: &'cx Object<'cx>) -> PlainObjectDisplay<'cx> {
PlainObjectDisplay { cx, object, cfg }
}

#[must_use]
pub struct PlainObjectDisplay<'cx> {
cx: &'cx Context,
object: &'cx Object<'cx>,
Expand All @@ -137,24 +136,24 @@ impl Display for PlainObjectDisplay<'_> {

if self.cfg.multiline {
f.write_str(NEWLINE)?;
let inner = INDENT.repeat((self.cfg.indentation + self.cfg.depth + 1) as usize);
let inner = indent_str((self.cfg.indentation + self.cfg.depth + 1) as usize);

for key in keys {
f.write_str(&inner)?;
inner.fmt(f)?;
let desc = self.object.get_descriptor(self.cx, &key).unwrap();
write_key_descriptor(f, self.cx, self.cfg, &key, &desc)?;
write_key_descriptor(f, self.cx, self.cfg, &key, &desc, self.object)?;
",".color(colour).fmt(f)?;
f.write_str(NEWLINE)?;
}

f.write_str(&INDENT.repeat((self.cfg.indentation + self.cfg.depth) as usize))?;
indent_str((self.cfg.indentation + self.cfg.depth) as usize).fmt(f)?;
} else {
f.write_char(' ')?;
let len = length.clamp(0, 3);

for (i, key) in keys.enumerate() {
let desc = self.object.get_descriptor(self.cx, &key).unwrap();
write_key_descriptor(f, self.cx, self.cfg, &key, &desc)?;
write_key_descriptor(f, self.cx, self.cfg, &key, &desc, self.object)?;

if i != len - 1 {
",".color(colour).fmt(f)?;
Expand All @@ -175,11 +174,11 @@ impl Display for PlainObjectDisplay<'_> {
}

fn write_key_descriptor(
f: &mut Formatter, cx: &Context, cfg: Config, key: &PropertyKey, desc: &PropertyDescriptor,
f: &mut Formatter, cx: &Context, cfg: Config, key: &PropertyKey, desc: &PropertyDescriptor, object: &Object,
) -> fmt::Result {
format_key(cx, cfg, &key.to_owned_key(cx)).fmt(f)?;
": ".color(cfg.colours.object).fmt(f)?;
format_descriptor(cx, cfg, desc).fmt(f)
format_descriptor(cx, cfg, desc, Some(object)).fmt(f)
}

pub(crate) fn write_remaining(f: &mut Formatter, remaining: usize, inner: Option<&str>, colour: Color) -> fmt::Result {
Expand Down
5 changes: 3 additions & 2 deletions ion/src/format/primitive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ use crate::conversions::FromValue;
use crate::format::Config;
use crate::format::symbol::format_symbol;

/// Formats a primitive value as a string using the given [configuration](Config).
/// Formats a primitive value using the given [configuration](Config).
/// The supported types are `boolean`, `number`, `string`, `symbol`, `null` and `undefined`.
pub fn format_primitive<'cx>(cx: &'cx Context, cfg: Config, value: &'cx Value<'cx>) -> PrimitiveDisplay<'cx> {
PrimitiveDisplay { cx, value, cfg }
}

#[must_use]
pub struct PrimitiveDisplay<'cx> {
cx: &'cx Context,
value: &'cx Value<'cx>,
Expand Down Expand Up @@ -54,7 +55,7 @@ impl Display for PrimitiveDisplay<'_> {
if self.cfg.quoted {
write!(f, "{0}{1}{0}", r#"""#.color(colours.string), str.color(colours.string))
} else {
f.write_str(&str)
str.fmt(f)
}
} else if value.is_null() {
"null".color(colours.null).fmt(f)
Expand Down
Loading

0 comments on commit 403dd73

Please sign in to comment.