Skip to content

Commit

Permalink
fix(format): prevent subtract with overflow in compact mode (#88)
Browse files Browse the repository at this point in the history
Fixes #87
  • Loading branch information
martinohmann committed Oct 12, 2022
1 parent 2a40b8f commit 7dd8e90
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 21 deletions.
16 changes: 8 additions & 8 deletions src/format/impls.rs
Expand Up @@ -294,17 +294,17 @@ impl Format for FuncCall {
self.name.format(fmt)?;
fmt.write_all(b"(")?;

fmt.compact_mode(true);
fmt.with_compact_mode(|fmt| {
for (i, arg) in self.args.iter().enumerate() {
if i > 0 {
fmt.write_all(b", ")?;
}

for (i, arg) in self.args.iter().enumerate() {
if i > 0 {
fmt.write_all(b", ")?;
arg.format(fmt)?;
}

arg.format(fmt)?;
}

fmt.compact_mode(false);
Ok(())
})?;

if self.expand_final {
fmt.write_all(b"...)")?;
Expand Down
40 changes: 27 additions & 13 deletions src/format/mod.rs
Expand Up @@ -36,6 +36,8 @@

mod escape;
mod impls;
#[cfg(test)]
mod tests;

use self::escape::{CharEscape, ESCAPE};
use crate::Result;
Expand Down Expand Up @@ -145,7 +147,7 @@ pub struct Formatter<'a, W> {
first_element: bool,
current_indent: usize,
has_value: bool,
compact_mode: bool,
compact_mode_level: u64,
}

/// A builder to create a `Formatter`.
Expand Down Expand Up @@ -180,7 +182,7 @@ impl<'a, W> FormatterBuilder<'a, W> {
first_element: false,
current_indent: 0,
has_value: false,
compact_mode: false,
compact_mode_level: 0,
}
}
}
Expand Down Expand Up @@ -305,7 +307,7 @@ where

/// Signals the start of an array to the formatter.
fn begin_array(&mut self) -> io::Result<()> {
if !self.compact_mode {
if !self.in_compact_mode() {
self.current_indent += 1;
}
self.has_value = false;
Expand All @@ -317,10 +319,10 @@ where
fn begin_array_value(&mut self) -> io::Result<()> {
if self.first_element {
self.first_element = false;
if !self.compact_mode {
if !self.in_compact_mode() {
self.write_all(b"\n")?;
}
} else if self.compact_mode {
} else if self.in_compact_mode() {
self.write_all(b", ")?;
} else {
self.write_all(b",\n")?;
Expand All @@ -337,7 +339,7 @@ where

/// Signals the end of an array to the formatter.
fn end_array(&mut self) -> io::Result<()> {
if !self.compact_mode {
if !self.in_compact_mode() {
self.current_indent -= 1;

if self.has_value {
Expand All @@ -351,7 +353,7 @@ where

/// Signals the start of an object to the formatter.
fn begin_object(&mut self) -> io::Result<()> {
if !self.compact_mode {
if !self.in_compact_mode() {
self.current_indent += 1;
}
self.has_value = false;
Expand All @@ -360,7 +362,7 @@ where

/// Signals the start of an object key to the formatter.
fn begin_object_key(&mut self) -> io::Result<()> {
if !self.compact_mode {
if !self.in_compact_mode() {
self.write_all(b"\n")?;
self.write_indent(self.current_indent)?;
}
Expand All @@ -380,7 +382,7 @@ where

/// Signals the end of an object to the formatter.
fn end_object(&mut self) -> io::Result<()> {
if !self.compact_mode {
if !self.in_compact_mode() {
self.current_indent -= 1;

if self.has_value {
Expand Down Expand Up @@ -479,10 +481,22 @@ where
Ok(())
}

/// Enables compact mode for the formatter. This is mostly used while serializing array and
/// object function arguments.
fn compact_mode(&mut self, yes: bool) {
self.compact_mode = yes;
/// Enables compact mode, runs the closure and disables compact mode again unless it's enabled
/// via another call to `with_compact_mode`.
///
/// This is mostly used for serializing array and object function arguments.
fn with_compact_mode<F>(&mut self, f: F) -> Result<()>
where
F: FnOnce(&mut Self) -> Result<()>,
{
self.compact_mode_level += 1;
let result = f(self);
self.compact_mode_level -= 1;
result
}

fn in_compact_mode(&self) -> bool {
self.compact_mode_level > 0
}
}

Expand Down
15 changes: 15 additions & 0 deletions src/format/tests.rs
@@ -0,0 +1,15 @@
use super::*;
use crate::{Expression, FuncCall};

#[test]
fn issue_87() {
let expr = Expression::from(
FuncCall::builder("foo")
.arg(Expression::from_iter([("bar", FuncCall::new("baz"))]))
.build(),
);

let result = to_string(&expr).unwrap();

assert_eq!(result, "foo({\"bar\" = baz()})")
}

0 comments on commit 7dd8e90

Please sign in to comment.