Add pretty printing for SQL Query
, Statement
, Value
, Array
, and Object
types (#1420)
This commit is contained in:
parent
79d6107b4e
commit
a1b9201bbd
10 changed files with 405 additions and 80 deletions
|
@ -5,7 +5,7 @@ use crate::err::Error;
|
||||||
use crate::sql::comment::mightbespace;
|
use crate::sql::comment::mightbespace;
|
||||||
use crate::sql::common::commas;
|
use crate::sql::common::commas;
|
||||||
use crate::sql::error::IResult;
|
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::number::Number;
|
||||||
use crate::sql::operation::Operation;
|
use crate::sql::operation::Operation;
|
||||||
use crate::sql::serde::is_internal_serialization;
|
use crate::sql::serde::is_internal_serialization;
|
||||||
|
@ -16,7 +16,7 @@ use nom::combinator::opt;
|
||||||
use nom::multi::separated_list0;
|
use nom::multi::separated_list0;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter, Write};
|
||||||
use std::ops;
|
use std::ops;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
|
@ -148,7 +148,12 @@ impl Array {
|
||||||
|
|
||||||
impl Display for Array {
|
impl Display for Array {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
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(']')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::cell::Cell;
|
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.
|
/// Implements fmt::Display by calling formatter on contents.
|
||||||
pub(crate) struct Fmt<T, F> {
|
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 {
|
pub(crate) fn comma_separated(into_iter: I) -> Self {
|
||||||
Self::new(into_iter, fmt_comma_separated)
|
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>(
|
fn fmt_comma_separated<T: Display>(
|
||||||
|
@ -37,9 +56,231 @@ fn fmt_comma_separated<T: Display>(
|
||||||
) -> fmt::Result {
|
) -> fmt::Result {
|
||||||
for (i, v) in into_iter.into_iter().enumerate() {
|
for (i, v) in into_iter.into_iter().enumerate() {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
|
// This comma goes after the item formatted in the last iteration.
|
||||||
f.write_str(", ")?;
|
f.write_str(", ")?;
|
||||||
}
|
}
|
||||||
Display::fmt(&v, f)?;
|
Display::fmt(&v, f)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
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]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::dbs::Transaction;
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
use crate::sql::common::commas;
|
use crate::sql::common::commas;
|
||||||
use crate::sql::error::IResult;
|
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::Next;
|
||||||
use crate::sql::part::{all, field, first, graph, index, last, part, thing, Part};
|
use crate::sql::part::{all, field, first, graph, index, last, part, thing, Part};
|
||||||
use crate::sql::paths::{ID, IN, OUT};
|
use crate::sql::paths::{ID, IN, OUT};
|
||||||
|
@ -147,18 +147,17 @@ impl Idiom {
|
||||||
|
|
||||||
impl fmt::Display for Idiom {
|
impl fmt::Display for Idiom {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
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,
|
f,
|
||||||
"{}",
|
|
||||||
self.0
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, p)| match (i, p) {
|
|
||||||
(0, Part::Field(v)) => format!("{}", v),
|
|
||||||
_ => format!("{}", p),
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join("")
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::sql::comment::mightbespace;
|
||||||
use crate::sql::common::{commas, val_char};
|
use crate::sql::common::{commas, val_char};
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::escape::escape_key;
|
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::operation::{Op, Operation};
|
||||||
use crate::sql::serde::is_internal_serialization;
|
use crate::sql::serde::is_internal_serialization;
|
||||||
use crate::sql::thing::Thing;
|
use crate::sql::thing::Thing;
|
||||||
|
@ -22,7 +22,7 @@ use serde::ser::SerializeMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt::{self, Display, Formatter, Write};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
|
|
||||||
|
@ -134,17 +134,33 @@ impl Object {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Object {
|
impl Display for Object {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
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!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"{{ {} }}",
|
"{}",
|
||||||
Fmt::comma_separated(
|
Fmt::pretty_comma_separated(
|
||||||
self.0.iter().map(|args| Fmt::new(args, |(k, v), f| {
|
self.0.iter().map(|args| Fmt::new(args, |(k, v), f| write!(
|
||||||
write!(f, "{}: {}", escape_key(k), v)
|
f,
|
||||||
}))
|
"{}: {}",
|
||||||
|
escape_key(k),
|
||||||
|
v
|
||||||
|
))),
|
||||||
)
|
)
|
||||||
)
|
)?;
|
||||||
|
drop(indent);
|
||||||
|
if is_pretty() {
|
||||||
|
f.write_char('}')
|
||||||
|
} else {
|
||||||
|
f.write_str(" }")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,17 @@ use crate::sql::comment::shouldbespace;
|
||||||
use crate::sql::common::commas;
|
use crate::sql::common::commas;
|
||||||
use crate::sql::common::commasorspace;
|
use crate::sql::common::commasorspace;
|
||||||
use crate::sql::error::IResult;
|
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 crate::sql::value::{value, Value};
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
use nom::combinator::map;
|
use nom::combinator::map;
|
||||||
use nom::{multi::separated_list0, sequence::tuple};
|
use nom::{multi::separated_list0, sequence::tuple};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt::Write;
|
||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
||||||
|
@ -53,8 +57,8 @@ impl Permissions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Permissions {
|
impl Display for Permissions {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "PERMISSIONS")?;
|
write!(f, "PERMISSIONS")?;
|
||||||
if self.is_none() {
|
if self.is_none() {
|
||||||
return write!(f, " NONE");
|
return write!(f, " NONE");
|
||||||
|
@ -62,11 +66,56 @@ impl fmt::Display for Permissions {
|
||||||
if self.is_full() {
|
if self.is_full() {
|
||||||
return write!(f, " FULL");
|
return write!(f, " FULL");
|
||||||
}
|
}
|
||||||
write!(
|
let mut lines = Vec::<(Vec<char>, &Permission)>::new();
|
||||||
f,
|
for (c, permission) in ['s', 'c', 'u', 'd'].into_iter().zip([
|
||||||
" FOR select {}, FOR create {}, FOR update {}, FOR delete {}",
|
&self.select,
|
||||||
self.select, self.create, self.update, self.delete
|
&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 {
|
impl Display for Permission {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::None => f.write_str("NONE"),
|
Self::None => f.write_str("NONE"),
|
||||||
Self::Full => f.write_str("FULL"),
|
Self::Full => f.write_str("FULL"),
|
||||||
|
@ -212,7 +261,7 @@ mod tests {
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let out = res.unwrap().1;
|
let out = res.unwrap().1;
|
||||||
assert_eq!(
|
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)
|
format!("{}", out)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
|
use crate::sql::fmt::Pretty;
|
||||||
use crate::sql::statement::{statements, Statement, Statements};
|
use crate::sql::statement::{statements, Statement, Statements};
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::combinator::all_consuming;
|
use nom::combinator::all_consuming;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fmt::Write;
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
@ -19,7 +21,7 @@ impl Deref for Query {
|
||||||
|
|
||||||
impl Display for Query {
|
impl Display for Query {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
Display::fmt(&self.0, f)
|
write!(Pretty::from(f), "{}", &self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ use crate::err::Error;
|
||||||
use crate::sql::comment::{comment, mightbespace};
|
use crate::sql::comment::{comment, mightbespace};
|
||||||
use crate::sql::common::colons;
|
use crate::sql::common::colons;
|
||||||
use crate::sql::error::IResult;
|
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::begin::{begin, BeginStatement};
|
||||||
use crate::sql::statements::cancel::{cancel, CancelStatement};
|
use crate::sql::statements::cancel::{cancel, CancelStatement};
|
||||||
use crate::sql::statements::commit::{commit, CommitStatement};
|
use crate::sql::statements::commit::{commit, CommitStatement};
|
||||||
|
@ -31,7 +33,7 @@ use nom::multi::many0;
|
||||||
use nom::multi::separated_list1;
|
use nom::multi::separated_list1;
|
||||||
use nom::sequence::delimited;
|
use nom::sequence::delimited;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter, Write};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
@ -48,7 +50,9 @@ impl Deref for Statements {
|
||||||
impl fmt::Display for Statements {
|
impl fmt::Display for Statements {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
Display::fmt(
|
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,
|
f,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -148,25 +152,25 @@ impl Statement {
|
||||||
impl Display for Statement {
|
impl Display for Statement {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Use(v) => Display::fmt(v, f),
|
Self::Use(v) => write!(Pretty::from(f), "{}", v),
|
||||||
Self::Set(v) => Display::fmt(v, f),
|
Self::Set(v) => write!(Pretty::from(f), "{}", v),
|
||||||
Self::Info(v) => Display::fmt(v, f),
|
Self::Info(v) => write!(Pretty::from(f), "{}", v),
|
||||||
Self::Live(v) => Display::fmt(v, f),
|
Self::Live(v) => write!(Pretty::from(f), "{}", v),
|
||||||
Self::Kill(v) => Display::fmt(v, f),
|
Self::Kill(v) => write!(Pretty::from(f), "{}", v),
|
||||||
Self::Begin(v) => Display::fmt(v, f),
|
Self::Begin(v) => write!(Pretty::from(f), "{}", v),
|
||||||
Self::Cancel(v) => Display::fmt(v, f),
|
Self::Cancel(v) => write!(Pretty::from(f), "{}", v),
|
||||||
Self::Commit(v) => Display::fmt(v, f),
|
Self::Commit(v) => write!(Pretty::from(f), "{}", v),
|
||||||
Self::Output(v) => Display::fmt(v, f),
|
Self::Output(v) => write!(Pretty::from(f), "{}", v),
|
||||||
Self::Ifelse(v) => Display::fmt(v, f),
|
Self::Ifelse(v) => write!(Pretty::from(f), "{}", v),
|
||||||
Self::Select(v) => Display::fmt(v, f),
|
Self::Select(v) => write!(Pretty::from(f), "{}", v),
|
||||||
Self::Create(v) => Display::fmt(v, f),
|
Self::Create(v) => write!(Pretty::from(f), "{}", v),
|
||||||
Self::Update(v) => Display::fmt(v, f),
|
Self::Update(v) => write!(Pretty::from(f), "{}", v),
|
||||||
Self::Relate(v) => Display::fmt(v, f),
|
Self::Relate(v) => write!(Pretty::from(f), "{}", v),
|
||||||
Self::Delete(v) => Display::fmt(v, f),
|
Self::Delete(v) => write!(Pretty::from(f), "{}", v),
|
||||||
Self::Insert(v) => Display::fmt(v, f),
|
Self::Insert(v) => write!(Pretty::from(f), "{}", v),
|
||||||
Self::Define(v) => Display::fmt(v, f),
|
Self::Define(v) => write!(Pretty::from(f), "{}", v),
|
||||||
Self::Remove(v) => Display::fmt(v, f),
|
Self::Remove(v) => write!(Pretty::from(f), "{}", v),
|
||||||
Self::Option(v) => Display::fmt(v, f),
|
Self::Option(v) => write!(Pretty::from(f), "{}", v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ use crate::sql::comment::shouldbespace;
|
||||||
use crate::sql::duration::{duration, Duration};
|
use crate::sql::duration::{duration, Duration};
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::escape::escape_str;
|
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::ident::{ident, Ident};
|
||||||
use crate::sql::idiom;
|
use crate::sql::idiom;
|
||||||
use crate::sql::idiom::{Idiom, Idioms};
|
use crate::sql::idiom::{Idiom, Idioms};
|
||||||
|
@ -31,8 +33,7 @@ use rand::distributions::Alphanumeric;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt::{self, Display, Write};
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
|
||||||
pub enum DefineStatement {
|
pub enum DefineStatement {
|
||||||
|
@ -709,19 +710,24 @@ impl fmt::Display for DefineTableStatement {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "DEFINE TABLE {}", self.name)?;
|
write!(f, "DEFINE TABLE {}", self.name)?;
|
||||||
if self.drop {
|
if self.drop {
|
||||||
write!(f, " DROP")?
|
f.write_str(" DROP")?;
|
||||||
}
|
|
||||||
if self.full {
|
|
||||||
write!(f, " SCHEMAFULL")?
|
|
||||||
}
|
|
||||||
if !self.full {
|
|
||||||
write!(f, " SCHEMALESS")?
|
|
||||||
}
|
}
|
||||||
|
f.write_str(if self.full {
|
||||||
|
" SCHEMAFULL"
|
||||||
|
} else {
|
||||||
|
" SCHEMALESS"
|
||||||
|
})?;
|
||||||
if let Some(ref v) = self.view {
|
if let Some(ref v) = self.view {
|
||||||
write!(f, " {}", v)?
|
write!(f, " {}", v)?
|
||||||
}
|
}
|
||||||
if !self.permissions.is_full() {
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ use crate::dbs::Transaction;
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
|
use crate::sql::fmt::{fmt_separated_by, Fmt};
|
||||||
use crate::sql::value::{value, Value};
|
use crate::sql::value::{value, Value};
|
||||||
use derive::Store;
|
use derive::Store;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
@ -11,6 +12,7 @@ use nom::combinator::opt;
|
||||||
use nom::multi::separated_list0;
|
use nom::multi::separated_list0;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Store, Hash)]
|
||||||
pub struct IfelseStatement {
|
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 {
|
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,
|
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 {
|
if let Some(ref v) = self.close {
|
||||||
write!(f, " ELSE {}", v)?
|
write!(f, " ELSE {}", v)?
|
||||||
}
|
}
|
||||||
write!(f, " END")?;
|
f.write_str(" END")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ use crate::sql::duration::{duration, Duration};
|
||||||
use crate::sql::edges::{edges, Edges};
|
use crate::sql::edges::{edges, Edges};
|
||||||
use crate::sql::error::IResult;
|
use crate::sql::error::IResult;
|
||||||
use crate::sql::expression::{expression, Expression};
|
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::function::{function, Function};
|
||||||
use crate::sql::future::{future, Future};
|
use crate::sql::future::{future, Future};
|
||||||
use crate::sql::geometry::{geometry, Geometry};
|
use crate::sql::geometry::{geometry, Geometry};
|
||||||
|
@ -49,7 +49,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter, Write};
|
||||||
use std::ops;
|
use std::ops;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
@ -1284,6 +1284,7 @@ impl Value {
|
||||||
|
|
||||||
impl fmt::Display for Value {
|
impl fmt::Display for Value {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let mut f = Pretty::from(f);
|
||||||
match self {
|
match self {
|
||||||
Value::None => write!(f, "NONE"),
|
Value::None => write!(f, "NONE"),
|
||||||
Value::Null => write!(f, "NULL"),
|
Value::Null => write!(f, "NULL"),
|
||||||
|
|
Loading…
Reference in a new issue