Add pretty printing for SQL Query, Statement, Value, Array, and Object types (#1420)

This commit is contained in:
Finn Bear 2023-01-19 01:53:33 -08:00 committed by GitHub
parent 79d6107b4e
commit a1b9201bbd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 405 additions and 80 deletions

View file

@ -5,7 +5,7 @@ use crate::err::Error;
use crate::sql::comment::mightbespace;
use crate::sql::common::commas;
use crate::sql::error::IResult;
use crate::sql::fmt::Fmt;
use crate::sql::fmt::{pretty_indent, Fmt, Pretty};
use crate::sql::number::Number;
use crate::sql::operation::Operation;
use crate::sql::serde::is_internal_serialization;
@ -16,7 +16,7 @@ use nom::combinator::opt;
use nom::multi::separated_list0;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::fmt::{self, Display, Formatter};
use std::fmt::{self, Display, Formatter, Write};
use std::ops;
use std::ops::Deref;
use std::ops::DerefMut;
@ -148,7 +148,12 @@ impl Array {
impl Display for Array {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "[{}]", Fmt::comma_separated(self.as_slice()))
let mut f = Pretty::from(f);
f.write_char('[')?;
let indent = pretty_indent();
write!(f, "{}", Fmt::pretty_comma_separated(self.as_slice()))?;
drop(indent);
f.write_char(']')
}
}

View file

@ -1,5 +1,6 @@
use std::cell::Cell;
use std::fmt::{self, Display, Formatter};
use std::fmt::{self, Display, Formatter, Write};
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
/// Implements fmt::Display by calling formatter on contents.
pub(crate) struct Fmt<T, F> {
@ -29,6 +30,24 @@ impl<I: IntoIterator<Item = T>, T: Display> Fmt<I, fn(I, &mut Formatter) -> fmt:
pub(crate) fn comma_separated(into_iter: I) -> Self {
Self::new(into_iter, fmt_comma_separated)
}
/// Formats values with a comma and a space separating them or, if pretty printing is in
/// effect, a comma, a newline, and indentation.
pub(crate) fn pretty_comma_separated(into_iter: I) -> Self {
Self::new(
into_iter,
if is_pretty() {
fmt_pretty_comma_separated
} else {
fmt_comma_separated
},
)
}
/// Formats values with a new line separating them.
pub(crate) fn pretty_new_line_separated(into_iter: I) -> Self {
Self::new(into_iter, fmt_new_line_separated)
}
}
fn fmt_comma_separated<T: Display>(
@ -37,9 +56,231 @@ fn fmt_comma_separated<T: Display>(
) -> fmt::Result {
for (i, v) in into_iter.into_iter().enumerate() {
if i > 0 {
// This comma goes after the item formatted in the last iteration.
f.write_str(", ")?;
}
Display::fmt(&v, f)?;
}
Ok(())
}
fn fmt_pretty_comma_separated<T: Display>(
into_iter: impl IntoIterator<Item = T>,
f: &mut Formatter,
) -> fmt::Result {
for (i, v) in into_iter.into_iter().enumerate() {
if i > 0 {
// We don't need a space after the comma if we are going to have a newline.
f.write_char(',')?;
pretty_sequence_item();
}
Display::fmt(&v, f)?;
}
Ok(())
}
fn fmt_new_line_separated<T: Display>(
into_iter: impl IntoIterator<Item = T>,
f: &mut Formatter,
) -> fmt::Result {
for (i, v) in into_iter.into_iter().enumerate() {
if i > 0 {
// One of the few cases where the raw string data depends on is pretty i.e. we don't
// need a space after the comma if we are going to have a newline.
if is_pretty() {
pretty_sequence_item();
} else {
f.write_char('\n')?;
}
}
Display::fmt(&v, f)?;
}
Ok(())
}
/// Creates a formatting function that joins iterators with an arbitrary separator.
pub fn fmt_separated_by<T: Display, II: IntoIterator<Item = T>>(
separator: impl Display,
) -> impl Fn(II, &mut Formatter) -> fmt::Result {
move |into_iter: II, f: &mut Formatter| {
for (i, v) in into_iter.into_iter().enumerate() {
if i > 0 {
// This separator goes after the item formatted in the last iteration.
Display::fmt(&separator, f)?;
}
Display::fmt(&v, f)?;
}
Ok(())
}
}
thread_local! {
// Avoid `RefCell`/`UnsafeCell` by using atomic types. Access is synchronized due to
// `thread_local!` so all accesses can use `Ordering::Relaxed`.
/// Whether pretty-printing.
static PRETTY: AtomicBool = AtomicBool::new(false);
/// The current level of indentation, in units of tabs.
static INDENT: AtomicU32 = AtomicU32::new(0);
/// Whether the next formatting action should be preceded by a newline and indentation.
static NEW_LINE: AtomicBool = AtomicBool::new(false);
}
/// An adapter that, if enabled, adds pretty print formatting.
pub(crate) struct Pretty<W: std::fmt::Write> {
inner: W,
/// This is the active pretty printer, responsible for injecting formatting.
active: bool,
}
impl<W: std::fmt::Write> Pretty<W> {
#[allow(unused)]
pub fn new(inner: W) -> Self {
Self::conditional(inner, true)
}
pub fn conditional(inner: W, enable: bool) -> Self {
let pretty_started_here = enable
&& PRETTY.with(|pretty| {
// Evaluates to true if PRETTY was false and is now true.
pretty.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed).is_ok()
});
if pretty_started_here {
// Clean slate.
NEW_LINE.with(|new_line| new_line.store(false, Ordering::Relaxed));
INDENT.with(|indent| indent.store(0, Ordering::Relaxed));
}
Self {
inner,
// Don't want multiple active pretty printers, although they wouldn't necessarily misbehave.
active: pretty_started_here,
}
}
}
impl<'a, 'b> From<&'a mut Formatter<'b>> for Pretty<&'a mut Formatter<'b>> {
fn from(f: &'a mut Formatter<'b>) -> Self {
Self::conditional(f, f.alternate())
}
}
impl<W: std::fmt::Write> Drop for Pretty<W> {
fn drop(&mut self) {
if self.active {
PRETTY.with(|pretty| {
debug_assert!(pretty.load(Ordering::Relaxed), "pretty status changed unexpectedly");
pretty.store(false, Ordering::Relaxed);
});
}
}
}
/// Returns whether pretty printing is in effect.
pub(crate) fn is_pretty() -> bool {
PRETTY.with(|pretty| pretty.load(Ordering::Relaxed))
}
/// If pretty printing is in effect, increments the indentation level (until the return value
/// is dropped).
#[must_use = "hold for the span of the indent, then drop"]
pub(crate) fn pretty_indent() -> PrettyGuard {
PrettyGuard::new(1)
}
/// Marks the end of an item in the sequence, after which indentation will follow if pretty printing
/// is in effect.
pub(crate) fn pretty_sequence_item() {
// List items need a new line, but no additional indentation.
NEW_LINE.with(|new_line| new_line.store(true, Ordering::Relaxed));
}
/// When dropped, applies the opposite increment to the current indentation level.
pub(crate) struct PrettyGuard {
increment: i8,
}
impl PrettyGuard {
fn new(increment: i8) -> Self {
Self::raw(increment);
PrettyGuard {
increment,
}
}
fn raw(increment: i8) {
INDENT.with(|indent| {
// Equivalent to `indent += increment` if signed numbers could be added to unsigned
// numbers in stable, atomic Rust.
if increment >= 0 {
indent.fetch_add(increment as u32, Ordering::Relaxed);
} else {
indent.fetch_sub(increment.unsigned_abs() as u32, Ordering::Relaxed);
}
});
NEW_LINE.with(|new_line| new_line.store(true, Ordering::Relaxed));
}
}
impl Drop for PrettyGuard {
fn drop(&mut self) {
Self::raw(-self.increment)
}
}
impl<W: std::fmt::Write> std::fmt::Write for Pretty<W> {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
if self.active && NEW_LINE.with(|new_line| new_line.swap(false, Ordering::Relaxed)) {
// Newline.
self.inner.write_char('\n')?;
for _ in 0..INDENT.with(|indent| indent.load(Ordering::Relaxed)) {
// One level of indentation.
self.inner.write_char('\t')?;
}
}
// What we were asked to write.
self.inner.write_str(s)
}
}
#[cfg(test)]
mod tests {
use crate::sql::{array::array, object::object, parse, value::value};
#[test]
fn pretty_query() {
let query = parse("SELECT * FROM {foo: [1, 2, 3]};").unwrap();
assert_eq!(format!("{}", query), "SELECT * FROM { foo: [1, 2, 3] };");
assert_eq!(
format!("{:#}", query),
"SELECT * FROM {\n\tfoo: [\n\t\t1,\n\t\t2,\n\t\t3\n\t]\n};"
);
}
#[test]
fn pretty_define_query() {
let query = parse("DEFINE TABLE test SCHEMAFULL PERMISSIONS FOR create, update, delete NONE FOR select WHERE public = true;").unwrap();
assert_eq!(format!("{}", query), "DEFINE TABLE test SCHEMAFULL PERMISSIONS FOR select WHERE public = true, FOR create, update, delete NONE;");
assert_eq!(format!("{:#}", query), "DEFINE TABLE test SCHEMAFULL\n\tPERMISSIONS\n\t\tFOR select\n\t\t\tWHERE public = true\n\t\tFOR create, update, delete NONE\n;");
}
#[test]
fn pretty_value() {
let value = value("{foo: [1, 2, 3]};").unwrap().1;
assert_eq!(format!("{}", value), "{ foo: [1, 2, 3] }");
assert_eq!(format!("{:#}", value), "{\n\tfoo: [\n\t\t1,\n\t\t2,\n\t\t3\n\t]\n}");
}
#[test]
fn pretty_object() {
let object = object("{foo: [1, 2, 3]};").unwrap().1;
assert_eq!(format!("{}", object), "{ foo: [1, 2, 3] }");
assert_eq!(format!("{:#}", object), "{\n\tfoo: [\n\t\t1,\n\t\t2,\n\t\t3\n\t]\n}");
}
#[test]
fn pretty_array() {
let array = array("[1, 2, 3];").unwrap().1;
assert_eq!(format!("{}", array), "[1, 2, 3]");
assert_eq!(format!("{:#}", array), "[\n\t1,\n\t2,\n\t3\n]");
}
}

View file

@ -4,7 +4,7 @@ use crate::dbs::Transaction;
use crate::err::Error;
use crate::sql::common::commas;
use crate::sql::error::IResult;
use crate::sql::fmt::Fmt;
use crate::sql::fmt::{fmt_separated_by, Fmt};
use crate::sql::part::Next;
use crate::sql::part::{all, field, first, graph, index, last, part, thing, Part};
use crate::sql::paths::{ID, IN, OUT};
@ -147,18 +147,17 @@ impl Idiom {
impl fmt::Display for Idiom {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
Display::fmt(
&Fmt::new(
self.0.iter().enumerate().map(|args| {
Fmt::new(args, |(i, p), f| match (i, p) {
(0, Part::Field(v)) => Display::fmt(v, f),
_ => Display::fmt(p, f),
})
}),
fmt_separated_by(""),
),
f,
"{}",
self.0
.iter()
.enumerate()
.map(|(i, p)| match (i, p) {
(0, Part::Field(v)) => format!("{}", v),
_ => format!("{}", p),
})
.collect::<Vec<_>>()
.join("")
)
}
}

View file

@ -6,7 +6,7 @@ use crate::sql::comment::mightbespace;
use crate::sql::common::{commas, val_char};
use crate::sql::error::IResult;
use crate::sql::escape::escape_key;
use crate::sql::fmt::Fmt;
use crate::sql::fmt::{is_pretty, pretty_indent, Fmt, Pretty};
use crate::sql::operation::{Op, Operation};
use crate::sql::serde::is_internal_serialization;
use crate::sql::thing::Thing;
@ -22,7 +22,7 @@ use serde::ser::SerializeMap;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::fmt;
use std::fmt::{self, Display, Formatter, Write};
use std::ops::Deref;
use std::ops::DerefMut;
@ -134,17 +134,33 @@ impl Object {
}
}
impl fmt::Display for Object {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
impl Display for Object {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let mut f = Pretty::from(f);
if is_pretty() {
f.write_char('{')?;
} else {
f.write_str("{ ")?;
}
let indent = pretty_indent();
write!(
f,
"{{ {} }}",
Fmt::comma_separated(
self.0.iter().map(|args| Fmt::new(args, |(k, v), f| {
write!(f, "{}: {}", escape_key(k), v)
}))
"{}",
Fmt::pretty_comma_separated(
self.0.iter().map(|args| Fmt::new(args, |(k, v), f| write!(
f,
"{}: {}",
escape_key(k),
v
))),
)
)
)?;
drop(indent);
if is_pretty() {
f.write_char('}')
} else {
f.write_str(" }")
}
}
}

View file

@ -2,13 +2,17 @@ use crate::sql::comment::shouldbespace;
use crate::sql::common::commas;
use crate::sql::common::commasorspace;
use crate::sql::error::IResult;
use crate::sql::fmt::is_pretty;
use crate::sql::fmt::pretty_indent;
use crate::sql::fmt::pretty_sequence_item;
use crate::sql::value::{value, Value};
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::combinator::map;
use nom::{multi::separated_list0, sequence::tuple};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::fmt::Write;
use std::fmt::{self, Display, Formatter};
use std::str;
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Hash)]
@ -53,8 +57,8 @@ impl Permissions {
}
}
impl fmt::Display for Permissions {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
impl Display for Permissions {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "PERMISSIONS")?;
if self.is_none() {
return write!(f, " NONE");
@ -62,11 +66,56 @@ impl fmt::Display for Permissions {
if self.is_full() {
return write!(f, " FULL");
}
write!(
f,
" FOR select {}, FOR create {}, FOR update {}, FOR delete {}",
self.select, self.create, self.update, self.delete
)
let mut lines = Vec::<(Vec<char>, &Permission)>::new();
for (c, permission) in ['s', 'c', 'u', 'd'].into_iter().zip([
&self.select,
&self.create,
&self.update,
&self.delete,
]) {
if let Some((existing, _)) = lines.iter_mut().find(|(_, p)| *p == permission) {
existing.push(c);
} else {
lines.push((vec![c], permission));
}
}
let indent = if is_pretty() {
Some(pretty_indent())
} else {
f.write_char(' ')?;
None
};
for (i, (kinds, permission)) in lines.into_iter().enumerate() {
if i > 0 {
if is_pretty() {
pretty_sequence_item();
} else {
f.write_str(", ")?;
}
}
write!(f, "FOR ")?;
for (i, kind) in kinds.into_iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
f.write_str(match kind {
's' => "select",
'c' => "create",
'u' => "update",
'd' => "delete",
_ => unreachable!(),
})?;
}
match permission {
Permission::Specific(_) if is_pretty() => {
let _indent = pretty_indent();
Display::fmt(permission, f)?;
}
_ => write!(f, " {}", permission)?,
}
}
drop(indent);
Ok(())
}
}
@ -144,8 +193,8 @@ impl Default for Permission {
}
}
impl fmt::Display for Permission {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
impl Display for Permission {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::None => f.write_str("NONE"),
Self::Full => f.write_str("FULL"),
@ -212,7 +261,7 @@ mod tests {
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(
"PERMISSIONS FOR select FULL, FOR create WHERE public = true, FOR update WHERE public = true, FOR delete NONE",
"PERMISSIONS FOR select FULL, FOR create, update WHERE public = true, FOR delete NONE",
format!("{}", out)
);
assert_eq!(

View file

@ -1,8 +1,10 @@
use crate::sql::error::IResult;
use crate::sql::fmt::Pretty;
use crate::sql::statement::{statements, Statement, Statements};
use derive::Store;
use nom::combinator::all_consuming;
use serde::{Deserialize, Serialize};
use std::fmt::Write;
use std::fmt::{self, Display, Formatter};
use std::ops::Deref;
use std::str;
@ -19,7 +21,7 @@ impl Deref for Query {
impl Display for Query {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(&self.0, f)
write!(Pretty::from(f), "{}", &self.0)
}
}

View file

@ -5,6 +5,8 @@ use crate::err::Error;
use crate::sql::comment::{comment, mightbespace};
use crate::sql::common::colons;
use crate::sql::error::IResult;
use crate::sql::fmt::Fmt;
use crate::sql::fmt::Pretty;
use crate::sql::statements::begin::{begin, BeginStatement};
use crate::sql::statements::cancel::{cancel, CancelStatement};
use crate::sql::statements::commit::{commit, CommitStatement};
@ -31,7 +33,7 @@ use nom::multi::many0;
use nom::multi::separated_list1;
use nom::sequence::delimited;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
use std::fmt::{self, Display, Formatter, Write};
use std::ops::Deref;
use std::time::Duration;
@ -48,7 +50,9 @@ impl Deref for Statements {
impl fmt::Display for Statements {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(
&self.0.iter().map(|ref v| format!("{};", v)).collect::<Vec<_>>().join("\n"),
&Fmt::pretty_new_line_separated(
self.0.iter().map(|v| Fmt::new(v, |v, f| write!(f, "{};", v))),
),
f,
)
}
@ -148,25 +152,25 @@ impl Statement {
impl Display for Statement {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Use(v) => Display::fmt(v, f),
Self::Set(v) => Display::fmt(v, f),
Self::Info(v) => Display::fmt(v, f),
Self::Live(v) => Display::fmt(v, f),
Self::Kill(v) => Display::fmt(v, f),
Self::Begin(v) => Display::fmt(v, f),
Self::Cancel(v) => Display::fmt(v, f),
Self::Commit(v) => Display::fmt(v, f),
Self::Output(v) => Display::fmt(v, f),
Self::Ifelse(v) => Display::fmt(v, f),
Self::Select(v) => Display::fmt(v, f),
Self::Create(v) => Display::fmt(v, f),
Self::Update(v) => Display::fmt(v, f),
Self::Relate(v) => Display::fmt(v, f),
Self::Delete(v) => Display::fmt(v, f),
Self::Insert(v) => Display::fmt(v, f),
Self::Define(v) => Display::fmt(v, f),
Self::Remove(v) => Display::fmt(v, f),
Self::Option(v) => Display::fmt(v, f),
Self::Use(v) => write!(Pretty::from(f), "{}", v),
Self::Set(v) => write!(Pretty::from(f), "{}", v),
Self::Info(v) => write!(Pretty::from(f), "{}", v),
Self::Live(v) => write!(Pretty::from(f), "{}", v),
Self::Kill(v) => write!(Pretty::from(f), "{}", v),
Self::Begin(v) => write!(Pretty::from(f), "{}", v),
Self::Cancel(v) => write!(Pretty::from(f), "{}", v),
Self::Commit(v) => write!(Pretty::from(f), "{}", v),
Self::Output(v) => write!(Pretty::from(f), "{}", v),
Self::Ifelse(v) => write!(Pretty::from(f), "{}", v),
Self::Select(v) => write!(Pretty::from(f), "{}", v),
Self::Create(v) => write!(Pretty::from(f), "{}", v),
Self::Update(v) => write!(Pretty::from(f), "{}", v),
Self::Relate(v) => write!(Pretty::from(f), "{}", v),
Self::Delete(v) => write!(Pretty::from(f), "{}", v),
Self::Insert(v) => write!(Pretty::from(f), "{}", v),
Self::Define(v) => write!(Pretty::from(f), "{}", v),
Self::Remove(v) => write!(Pretty::from(f), "{}", v),
Self::Option(v) => write!(Pretty::from(f), "{}", v),
}
}
}

View file

@ -9,6 +9,8 @@ use crate::sql::comment::shouldbespace;
use crate::sql::duration::{duration, Duration};
use crate::sql::error::IResult;
use crate::sql::escape::escape_str;
use crate::sql::fmt::is_pretty;
use crate::sql::fmt::pretty_indent;
use crate::sql::ident::{ident, Ident};
use crate::sql::idiom;
use crate::sql::idiom::{Idiom, Idioms};
@ -31,8 +33,7 @@ use rand::distributions::Alphanumeric;
use rand::rngs::OsRng;
use rand::Rng;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::fmt::Display;
use std::fmt::{self, Display, Write};
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
pub enum DefineStatement {
@ -709,19 +710,24 @@ impl fmt::Display for DefineTableStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE TABLE {}", self.name)?;
if self.drop {
write!(f, " DROP")?
}
if self.full {
write!(f, " SCHEMAFULL")?
}
if !self.full {
write!(f, " SCHEMALESS")?
f.write_str(" DROP")?;
}
f.write_str(if self.full {
" SCHEMAFULL"
} else {
" SCHEMALESS"
})?;
if let Some(ref v) = self.view {
write!(f, " {}", v)?
}
if !self.permissions.is_full() {
write!(f, " {}", self.permissions)?;
let _indent = if is_pretty() {
Some(pretty_indent())
} else {
f.write_char(' ')?;
None
};
write!(f, "{}", self.permissions)?;
}
Ok(())
}

View file

@ -4,6 +4,7 @@ use crate::dbs::Transaction;
use crate::err::Error;
use crate::sql::comment::shouldbespace;
use crate::sql::error::IResult;
use crate::sql::fmt::{fmt_separated_by, Fmt};
use crate::sql::value::{value, Value};
use derive::Store;
use nom::bytes::complete::tag_no_case;
@ -11,6 +12,7 @@ use nom::combinator::opt;
use nom::multi::separated_list0;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::fmt::Display;
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
pub struct IfelseStatement {
@ -48,21 +50,21 @@ impl IfelseStatement {
}
}
impl fmt::Display for IfelseStatement {
impl Display for IfelseStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
Display::fmt(
&Fmt::new(
self.exprs.iter().map(|args| {
Fmt::new(args, |(cond, then), f| write!(f, "IF {} THEN {}", cond, then))
}),
fmt_separated_by(" ELSE "),
),
f,
"{}",
self.exprs
.iter()
.map(|(ref cond, ref then)| format!("IF {} THEN {}", cond, then))
.collect::<Vec<_>>()
.join(" ELSE ")
)?;
if let Some(ref v) = self.close {
write!(f, " ELSE {}", v)?
}
write!(f, " END")?;
f.write_str(" END")?;
Ok(())
}
}

View file

@ -12,7 +12,7 @@ use crate::sql::duration::{duration, Duration};
use crate::sql::edges::{edges, Edges};
use crate::sql::error::IResult;
use crate::sql::expression::{expression, Expression};
use crate::sql::fmt::Fmt;
use crate::sql::fmt::{Fmt, Pretty};
use crate::sql::function::{function, Function};
use crate::sql::future::{future, Future};
use crate::sql::geometry::{geometry, Geometry};
@ -49,7 +49,7 @@ use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::fmt::{self, Display, Formatter};
use std::fmt::{self, Display, Formatter, Write};
use std::ops;
use std::ops::Deref;
use std::str::FromStr;
@ -1284,6 +1284,7 @@ impl Value {
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut f = Pretty::from(f);
match self {
Value::None => write!(f, "NONE"),
Value::Null => write!(f, "NULL"),