Improve structured output ()

Co-authored-by: Gerard Guillemas Martos <gerard.guillemas@surrealdb.com>
Co-authored-by: Gerard Guillemas Martos <gguillemas@users.noreply.github.com>
This commit is contained in:
Tobie Morgan Hitchcock 2024-06-25 08:41:06 +01:00 committed by GitHub
parent 1810189f54
commit af80c1ccce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 309 additions and 552 deletions

View file

@ -11,10 +11,10 @@ macro_rules! bytes {
#[macro_export]
#[doc(hidden)]
macro_rules! map {
($($k:expr => $v:expr),* $(,)? $( => $x:expr )?) => {{
($($k:expr $(, if let $grant:pat = $check:expr)? $(, if $guard:expr)? => $v:expr),* $(,)? $( => $x:expr )?) => {{
let mut m = ::std::collections::BTreeMap::new();
$(m.extend($x.iter().map(|(k, v)| (k.clone(), v.clone())));)?
$(m.insert($k, $v);)+
$(m.extend($x.iter().map(|(k, v)| (k.clone(), v.clone())));)?
$( $(if let $grant = $check)? $(if $guard)? { m.insert($k, $v); };)+
m
}};
}

View file

@ -1,3 +1,4 @@
use super::Value;
use crate::sql::statements::info::InfoStructure;
use crate::sql::statements::DefineAccessStatement;
use crate::sql::{escape::quote_str, Algorithm};
@ -6,8 +7,6 @@ use serde::{Deserialize, Serialize};
use std::fmt;
use std::fmt::Display;
use super::{Object, Value};
/// The type of access methods available
#[revisioned(revision = 1)]
#[derive(Debug, Serialize, Deserialize, Hash, Clone, Eq, PartialEq, PartialOrd)]
@ -27,9 +26,47 @@ impl Default for AccessType {
}
}
impl Display for AccessType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AccessType::Jwt(ac) => {
write!(f, "JWT {}", ac)?;
}
AccessType::Record(ac) => {
f.write_str("RECORD")?;
if let Some(ref v) = ac.signup {
write!(f, " SIGNUP {v}")?
}
if let Some(ref v) = ac.signin {
write!(f, " SIGNIN {v}")?
}
write!(f, " WITH JWT {}", ac.jwt)?;
}
}
Ok(())
}
}
impl InfoStructure for AccessType {
fn structure(self) -> Value {
match self {
AccessType::Jwt(v) => Value::from(map! {
"kind".to_string() => "JWT".into(),
"jwt".to_string() => v.structure(),
}),
AccessType::Record(v) => Value::from(map! {
"kind".to_string() => "RECORD".into(),
"jwt".to_string() => v.jwt.structure(),
"signup".to_string(), if let Some(v) = v.signup => v.structure(),
"signin".to_string(), if let Some(v) = v.signin => v.structure(),
}),
}
}
}
impl AccessType {
// Returns whether or not the access method can issue non-token grants
// In this context, token refers exclusively to JWT
/// Returns whether or not the access method can issue non-token grants
/// In this context, token refers exclusively to JWT
#[allow(unreachable_patterns)]
pub fn can_issue_grants(&self) -> bool {
match self {
@ -39,8 +76,8 @@ impl AccessType {
_ => unreachable!(),
}
}
// Returns whether or not the access method can issue tokens
// In this context, tokens refers exclusively to JWT
/// Returns whether or not the access method can issue tokens
/// In this context, tokens refers exclusively to JWT
pub fn can_issue_tokens(&self) -> bool {
match self {
// The JWT access method can only issue tokens if an issuer is set
@ -80,7 +117,45 @@ impl Default for JwtAccess {
}
}
impl Display for JwtAccess {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.verify {
JwtAccessVerify::Key(ref v) => {
write!(f, "ALGORITHM {} KEY {}", v.alg, quote_str(&v.key))?;
}
JwtAccessVerify::Jwks(ref v) => {
write!(f, "URL {}", quote_str(&v.url),)?;
}
}
if let Some(iss) = &self.issue {
write!(f, " WITH ISSUER KEY {}", quote_str(&iss.key))?;
}
Ok(())
}
}
impl InfoStructure for JwtAccess {
fn structure(self) -> Value {
Value::from(map! {
"verify".to_string() => match self.verify {
JwtAccessVerify::Jwks(v) => Value::from(map!{
"url".to_string() => v.url.into(),
}),
JwtAccessVerify::Key(v) => Value::from(map!{
"alg".to_string() => v.alg.structure(),
"key".to_string() => v.key.into(),
}),
},
"issuer".to_string(), if let Some(v) = self.issue => Value::from(map!{
"alg".to_string() => v.alg.structure(),
"key".to_string() => v.key.into(),
}),
})
}
}
impl JwtAccess {
/// Redacts certain parts of the definition for security on export.
pub(crate) fn redacted(&self) -> JwtAccess {
let mut jwt = self.clone();
jwt.verify = match jwt.verify {
@ -141,6 +216,20 @@ impl Default for JwtAccessVerify {
}
}
impl InfoStructure for JwtAccessVerify {
fn structure(self) -> Value {
match self {
JwtAccessVerify::Jwks(v) => Value::from(map! {
"url".to_string() => v.url.into(),
}),
JwtAccessVerify::Key(v) => Value::from(map! {
"alg".to_string() => v.alg.structure(),
"key".to_string() => v.key.into(),
}),
}
}
}
#[revisioned(revision = 1)]
#[derive(Debug, Serialize, Deserialize, Hash, Clone, Eq, PartialEq, PartialOrd)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
@ -187,88 +276,3 @@ impl Default for RecordAccess {
}
}
}
impl Display for AccessType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AccessType::Jwt(ac) => {
write!(f, "JWT {}", ac)?;
}
AccessType::Record(ac) => {
f.write_str("RECORD")?;
if let Some(ref v) = ac.signup {
write!(f, " SIGNUP {v}")?
}
if let Some(ref v) = ac.signin {
write!(f, " SIGNIN {v}")?
}
write!(f, " WITH JWT {}", ac.jwt)?;
}
}
Ok(())
}
}
impl InfoStructure for AccessType {
fn structure(self) -> Value {
let mut acc = Object::default();
match self {
AccessType::Jwt(ac) => {
acc.insert("kind".to_string(), "JWT".into());
acc.insert("jwt".to_string(), ac.structure());
}
AccessType::Record(ac) => {
acc.insert("kind".to_string(), "RECORD".into());
if let Some(signup) = ac.signup {
acc.insert("signup".to_string(), signup.structure());
}
if let Some(signin) = ac.signin {
acc.insert("signin".to_string(), signin.structure());
}
acc.insert("jwt".to_string(), ac.jwt.structure());
}
};
Value::Object(acc)
}
}
impl Display for JwtAccess {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.verify {
JwtAccessVerify::Key(ref v) => {
write!(f, "ALGORITHM {} KEY {}", v.alg, quote_str(&v.key))?;
}
JwtAccessVerify::Jwks(ref v) => {
write!(f, "URL {}", quote_str(&v.url),)?;
}
}
if let Some(iss) = &self.issue {
write!(f, " WITH ISSUER KEY {}", quote_str(&iss.key))?;
}
Ok(())
}
}
impl InfoStructure for JwtAccess {
fn structure(self) -> Value {
let mut acc = Object::default();
match self.verify {
JwtAccessVerify::Key(v) => {
acc.insert("alg".to_string(), v.alg.structure());
acc.insert("key".to_string(), v.key.into());
}
JwtAccessVerify::Jwks(v) => {
acc.insert("url".to_string(), v.url.into());
}
}
if let Some(v) = self.issue {
let mut iss = Object::default();
iss.insert("alg".to_string(), v.alg.structure());
iss.insert("key".to_string(), v.key.into());
acc.insert("issuer".to_string(), iss.to_string().into());
}
Value::Object(acc)
}
}

View file

@ -76,6 +76,7 @@ impl fmt::Display for Algorithm {
})
}
}
impl InfoStructure for Algorithm {
fn structure(self) -> Value {
self.to_string().into()

View file

@ -33,6 +33,7 @@ impl fmt::Display for Base {
}
}
}
impl InfoStructure for Base {
fn structure(self) -> Value {
self.to_string().into()

View file

@ -33,8 +33,12 @@ impl Default for ChangeFeed {
}
}
}
impl InfoStructure for ChangeFeed {
fn structure(self) -> Value {
self.to_string().into()
Value::from(map! {
"expiry".to_string() => Duration(self.expiry).structure(),
"original".to_string() => self.store_diff.into(),
})
}
}

View file

@ -23,6 +23,7 @@ impl fmt::Display for Cond {
write!(f, "WHERE {}", self.0)
}
}
impl InfoStructure for Cond {
fn structure(self) -> Value {
self.0.structure()

View file

@ -1,5 +1,7 @@
use crate::sql::datetime::Datetime;
use crate::sql::statements::info::InfoStructure;
use crate::sql::strand::Strand;
use crate::sql::Value;
use crate::syn;
use revision::revisioned;
use serde::{Deserialize, Serialize};
@ -294,3 +296,9 @@ impl<'a> Sum<&'a Self> for Duration {
iter.fold(Duration::default(), |a, b| &a + b)
}
}
impl InfoStructure for Duration {
fn structure(self) -> Value {
self.to_string().into()
}
}

View file

@ -36,7 +36,7 @@ impl fmt::Display for Fetchs {
impl InfoStructure for Fetchs {
fn structure(self) -> Value {
Value::Array(self.0.into_iter().map(|f| f.0.structure()).collect())
self.into_iter().map(Fetch::structure).collect::<Vec<_>>().into()
}
}
@ -58,3 +58,9 @@ impl Display for Fetch {
Display::fmt(&self.0, f)
}
}
impl InfoStructure for Fetch {
fn structure(self) -> Value {
self.to_string().into()
}
}

View file

@ -72,6 +72,7 @@ impl InfoStructure for Fields {
self.to_string().into()
}
}
impl Fields {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(

View file

@ -62,6 +62,6 @@ impl Display for Ident {
impl InfoStructure for Ident {
fn structure(self) -> Value {
self.0.into()
self.to_string().into()
}
}

View file

@ -46,6 +46,12 @@ impl Display for Idioms {
}
}
impl InfoStructure for Idioms {
fn structure(self) -> Value {
self.to_string().into()
}
}
#[revisioned(revision = 1)]
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
#[serde(rename = "$surrealdb::private::sql::Idiom")]
@ -202,12 +208,6 @@ impl Display for Idiom {
}
}
impl InfoStructure for Idioms {
fn structure(self) -> Value {
self.to_string().into()
}
}
impl InfoStructure for Idiom {
fn structure(self) -> Value {
self.to_string().into()

View file

@ -2,7 +2,7 @@ use crate::sql::fmt::is_pretty;
use crate::sql::fmt::pretty_indent;
use crate::sql::fmt::pretty_sequence_item;
use crate::sql::statements::info::InfoStructure;
use crate::sql::{Object, Value};
use crate::sql::Value;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::Write;
@ -113,6 +113,17 @@ impl Display for Permissions {
}
}
impl InfoStructure for Permissions {
fn structure(self) -> Value {
Value::from(map! {
"select".to_string() => self.select.structure(),
"create".to_string() => self.create.structure(),
"update".to_string() => self.update.structure(),
"delete".to_string() => self.delete.structure(),
})
}
}
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub(crate) enum PermissionKind {
Select,
@ -173,26 +184,7 @@ impl InfoStructure for Permission {
match self {
Permission::None => Value::Bool(false),
Permission::Full => Value::Bool(true),
Permission::Specific(v) => Value::Strand(v.to_string().into()),
Permission::Specific(v) => v.to_string().into(),
}
}
}
impl InfoStructure for Permissions {
fn structure(self) -> Value {
let Self {
select,
create,
update,
delete,
} = self;
let mut acc = Object::default();
acc.insert("select".to_string(), select.structure());
acc.insert("create".to_string(), create.structure());
acc.insert("update".to_string(), update.structure());
acc.insert("delete".to_string(), delete.structure());
Value::Object(acc)
}
}

View file

@ -4,7 +4,7 @@ use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::statements::info::InfoStructure;
use crate::sql::{access::AccessDuration, AccessType, Base, Ident, Object, Strand, Value};
use crate::sql::{access::AccessDuration, AccessType, Base, Ident, Strand, Value};
use derive::Store;
use rand::distributions::Alphanumeric;
use rand::Rng;
@ -174,36 +174,16 @@ impl Display for DefineAccessStatement {
impl InfoStructure for DefineAccessStatement {
fn structure(self) -> Value {
let Self {
name,
base,
kind,
duration,
comment,
..
} = self;
let mut acc = Object::default();
acc.insert("name".to_string(), name.structure());
acc.insert("base".to_string(), base.structure());
let mut dur = Object::default();
if kind.can_issue_grants() {
dur.insert("grant".to_string(), duration.grant.into());
}
if kind.can_issue_tokens() {
dur.insert("token".to_string(), duration.token.into());
}
dur.insert("session".to_string(), duration.session.into());
acc.insert("duration".to_string(), dur.to_string().into());
acc.insert("kind".to_string(), kind.structure());
if let Some(comment) = comment {
acc.insert("comment".to_string(), comment.into());
}
Value::Object(acc)
Value::from(map! {
"name".to_string() => self.name.structure(),
"base".to_string() => self.base.structure(),
"duration".to_string() => Value::from(map!{
"session".to_string() => self.duration.session.into(),
"grant".to_string(), if self.kind.can_issue_grants() => self.duration.grant.into(),
"token".to_string(), if self.kind.can_issue_tokens() => self.duration.token.into(),
}),
"kind".to_string() => self.kind.structure(),
"comment".to_string(), if let Some(v) = self.comment => v.into(),
})
}
}

View file

@ -4,7 +4,7 @@ use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::statements::info::InfoStructure;
use crate::sql::{filter::Filter, tokenizer::Tokenizer, Base, Ident, Object, Strand, Value};
use crate::sql::{filter::Filter, tokenizer::Tokenizer, Array, Base, Ident, Strand, Value};
use derive::Store;
use revision::revisioned;
use serde::{Deserialize, Serialize};
@ -96,40 +96,14 @@ impl Display for DefineAnalyzerStatement {
impl InfoStructure for DefineAnalyzerStatement {
fn structure(self) -> Value {
let Self {
name,
function,
tokenizers,
filters,
comment,
..
} = self;
let mut acc = Object::default();
acc.insert("name".to_string(), name.structure());
if let Some(function) = function {
acc.insert("function".to_string(), function.structure());
}
if let Some(tokenizers) = tokenizers {
acc.insert(
"tokenizers".to_string(),
Value::Array(tokenizers.into_iter().map(|t| t.to_string().into()).collect()),
);
}
if let Some(filters) = filters {
acc.insert(
"filters".to_string(),
Value::Array(filters.into_iter().map(|f| f.to_string().into()).collect()),
);
}
if let Some(comment) = comment {
acc.insert("comment".to_string(), comment.into());
}
Value::Object(acc)
Value::from(map! {
"name".to_string() => self.name.structure(),
"function".to_string(), if let Some(v) = self.function => v.structure(),
"tokenizers".to_string(), if let Some(v) = self.tokenizers =>
v.into_iter().map(|v| v.to_string().into()).collect::<Array>().into(),
"filters".to_string(), if let Some(v) = self.filters =>
v.into_iter().map(|v| v.to_string().into()).collect::<Array>().into(),
"comment".to_string(), if let Some(v) = self.comment => v.into(),
})
}
}

View file

@ -4,7 +4,7 @@ use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::statements::info::InfoStructure;
use crate::sql::{changefeed::ChangeFeed, Base, Ident, Object, Strand, Value};
use crate::sql::{changefeed::ChangeFeed, Base, Ident, Strand, Value};
use derive::Store;
use revision::revisioned;
use serde::{Deserialize, Serialize};
@ -94,19 +94,9 @@ impl Display for DefineDatabaseStatement {
impl InfoStructure for DefineDatabaseStatement {
fn structure(self) -> Value {
let Self {
name,
comment,
..
} = self;
let mut acc = Object::default();
acc.insert("name".to_string(), name.structure());
if let Some(comment) = comment {
acc.insert("comment".to_string(), comment.into());
}
Value::Object(acc)
Value::from(map! {
"name".to_string() => self.name.structure(),
"comment".to_string(), if let Some(v) = self.comment => v.into(),
})
}
}

View file

@ -4,7 +4,7 @@ use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::statements::info::InfoStructure;
use crate::sql::{Base, Ident, Object, Strand, Value, Values};
use crate::sql::{Base, Ident, Strand, Value, Values};
use derive::Store;
use revision::revisioned;
use serde::{Deserialize, Serialize};
@ -85,31 +85,12 @@ impl Display for DefineEventStatement {
impl InfoStructure for DefineEventStatement {
fn structure(self) -> Value {
let Self {
name,
what,
when,
then,
comment,
..
} = self;
let mut acc = Object::default();
acc.insert("name".to_string(), name.structure());
acc.insert("what".to_string(), what.structure());
acc.insert("when".to_string(), when.structure());
acc.insert(
"then".to_string(),
Value::Array(then.0.iter().map(|v| v.to_string().into()).collect()),
);
if let Some(comment) = comment {
acc.insert("comment".to_string(), comment.into());
}
Value::Object(acc)
Value::from(map! {
"name".to_string() => self.name.structure(),
"what".to_string() => self.what.structure(),
"when".to_string() => self.when.structure(),
"then".to_string() => self.then.structure(),
"comment".to_string(), if let Some(v) = self.comment => v.into(),
})
}
}

View file

@ -3,12 +3,11 @@ use crate::dbs::Options;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::fmt::{is_pretty, pretty_indent};
use crate::sql::statements::info::InfoStructure;
use crate::sql::statements::DefineTableStatement;
use crate::sql::{
fmt::is_pretty, fmt::pretty_indent, Base, Ident, Idiom, Kind, Permissions, Strand, Value,
};
use crate::sql::{Object, Part};
use crate::sql::Part;
use crate::sql::{Base, Ident, Idiom, Kind, Permissions, Strand, Value};
use crate::sql::{Relation, TableType};
use derive::Store;
use revision::revisioned;
@ -210,51 +209,17 @@ impl Display for DefineFieldStatement {
impl InfoStructure for DefineFieldStatement {
fn structure(self) -> Value {
let Self {
name,
what,
flex,
kind,
readonly,
value,
assert,
default,
permissions,
comment,
..
} = self;
let mut acc = Object::default();
acc.insert("name".to_string(), name.structure());
acc.insert("what".to_string(), what.structure());
acc.insert("flex".to_string(), flex.into());
if let Some(kind) = kind {
acc.insert("kind".to_string(), kind.structure());
}
acc.insert("readonly".to_string(), readonly.into());
if let Some(value) = value {
acc.insert("value".to_string(), value.structure());
}
if let Some(assert) = assert {
acc.insert("assert".to_string(), assert.structure());
}
if let Some(default) = default {
acc.insert("default".to_string(), default.structure());
}
acc.insert("permissions".to_string(), permissions.structure());
if let Some(comment) = comment {
acc.insert("comment".to_string(), comment.into());
}
Value::Object(acc)
Value::from(map! {
"name".to_string() => self.name.structure(),
"what".to_string() => self.what.structure(),
"flex".to_string() => self.flex.into(),
"kind".to_string(), if let Some(v) = self.kind => v.structure(),
"value".to_string(), if let Some(v) = self.value => v.structure(),
"assert".to_string(), if let Some(v) = self.assert => v.structure(),
"default".to_string(), if let Some(v) = self.default => v.structure(),
"readonly".to_string() => self.readonly.into(),
"permissions".to_string() => self.permissions.structure(),
"comment".to_string(), if let Some(v) = self.comment => v.into(),
})
}
}

View file

@ -3,11 +3,9 @@ use crate::dbs::Options;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::fmt::{is_pretty, pretty_indent};
use crate::sql::statements::info::InfoStructure;
use crate::sql::{
fmt::{is_pretty, pretty_indent},
Base, Block, Ident, Kind, Object, Permission, Strand, Value,
};
use crate::sql::{Base, Block, Ident, Kind, Permission, Strand, Value};
use derive::Store;
use revision::revisioned;
use serde::{Deserialize, Serialize};
@ -100,36 +98,16 @@ impl fmt::Display for DefineFunctionStatement {
impl InfoStructure for DefineFunctionStatement {
fn structure(self) -> Value {
let Self {
name,
args,
block,
comment,
permissions,
..
} = self;
let mut acc = Object::default();
acc.insert("name".to_string(), name.structure());
acc.insert(
"args".to_string(),
Value::Array(
args.into_iter()
.map(|(n, k)| Value::Array(vec![n.structure(), k.structure()].into()))
.collect::<Vec<Value>>()
.into(),
),
);
acc.insert("block".to_string(), block.structure());
acc.insert("permissions".to_string(), permissions.structure());
if let Some(comment) = comment {
acc.insert("comment".to_string(), comment.into());
}
Value::Object(acc)
Value::from(map! {
"name".to_string() => self.name.structure(),
"args".to_string() => self.args
.into_iter()
.map(|(n, k)| vec![n.structure(), k.structure()].into())
.collect::<Vec<Value>>()
.into(),
"block".to_string() => self.block.structure(),
"permissions".to_string() => self.permissions.structure(),
"comment".to_string(), if let Some(v) = self.comment => v.into(),
})
}
}

View file

@ -5,7 +5,7 @@ use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::statements::info::InfoStructure;
use crate::sql::{
statements::UpdateStatement, Base, Ident, Idioms, Index, Object, Part, Strand, Value, Values,
statements::UpdateStatement, Base, Ident, Idioms, Index, Part, Strand, Value, Values,
};
use derive::Store;
use reblessive::tree::Stk;
@ -130,28 +130,12 @@ impl Display for DefineIndexStatement {
impl InfoStructure for DefineIndexStatement {
fn structure(self) -> Value {
let Self {
name,
what,
cols,
index,
comment,
..
} = self;
let mut acc = Object::default();
acc.insert("name".to_string(), name.structure());
acc.insert("what".to_string(), what.structure());
acc.insert("cols".to_string(), cols.structure());
acc.insert("index".to_string(), index.structure());
if let Some(comment) = comment {
acc.insert("comment".to_string(), comment.into());
}
Value::Object(acc)
Value::from(map! {
"name".to_string() => self.name.structure(),
"what".to_string() => self.what.structure(),
"cols".to_string() => self.cols.structure(),
"index".to_string() => self.index.structure(),
"comment".to_string(), if let Some(v) = self.comment => v.into(),
})
}
}

View file

@ -3,11 +3,9 @@ use crate::dbs::Options;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::fmt::{is_pretty, pretty_indent};
use crate::sql::statements::info::InfoStructure;
use crate::sql::{
fmt::{is_pretty, pretty_indent},
Base, Ident, Object, Permission, Strand, Value,
};
use crate::sql::{Base, Ident, Permission, Strand, Value};
use derive::Store;
use revision::revisioned;
use serde::{Deserialize, Serialize};
@ -94,25 +92,11 @@ impl DefineModelStatement {
impl InfoStructure for DefineModelStatement {
fn structure(self) -> Value {
let Self {
name,
version,
comment,
permissions,
..
} = self;
let mut acc = Object::default();
acc.insert("name".to_string(), name.structure());
acc.insert("version".to_string(), version.into());
if let Some(comment) = comment {
acc.insert("comment".to_string(), comment.into());
}
acc.insert("permissions".to_string(), permissions.structure());
Value::Object(acc)
Value::from(map! {
"name".to_string() => self.name.structure(),
"version".to_string() => self.version.into(),
"permissions".to_string() => self.permissions.structure(),
"comment".to_string(), if let Some(v) = self.comment => v.into(),
})
}
}

View file

@ -4,7 +4,7 @@ use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::statements::info::InfoStructure;
use crate::sql::{Base, Ident, Object, Strand, Value};
use crate::sql::{Base, Ident, Strand, Value};
use derive::Store;
use revision::revisioned;
use serde::{Deserialize, Serialize};
@ -87,19 +87,9 @@ impl Display for DefineNamespaceStatement {
impl InfoStructure for DefineNamespaceStatement {
fn structure(self) -> Value {
let Self {
name,
comment,
..
} = self;
let mut acc = Object::default();
acc.insert("name".to_string(), name.structure());
if let Some(comment) = comment {
acc.insert("comment".to_string(), comment.into());
}
Value::Object(acc)
Value::from(map! {
"name".to_string() => self.name.structure(),
"comment".to_string(), if let Some(v) = self.comment => v.into(),
})
}
}

View file

@ -5,7 +5,7 @@ use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::fmt::{is_pretty, pretty_indent};
use crate::sql::statements::info::InfoStructure;
use crate::sql::{Base, Ident, Object, Permission, Strand, Value};
use crate::sql::{Base, Ident, Permission, Strand, Value};
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
@ -93,25 +93,11 @@ impl Display for DefineParamStatement {
impl InfoStructure for DefineParamStatement {
fn structure(self) -> Value {
let Self {
name,
value,
comment,
permissions,
..
} = self;
let mut acc = Object::default();
acc.insert("name".to_string(), name.structure());
acc.insert("value".to_string(), value.structure());
if let Some(comment) = comment {
acc.insert("comment".to_string(), comment.into());
}
acc.insert("permissions".to_string(), permissions.structure());
Value::Object(acc)
Value::from(map! {
"name".to_string() => self.name.structure(),
"value".to_string() => self.value.structure(),
"permissions".to_string() => self.permissions.structure(),
"comment".to_string(), if let Some(v) = self.comment => v.into(),
})
}
}

View file

@ -1,25 +1,23 @@
use super::DefineFieldStatement;
use crate::ctx::Context;
use crate::dbs::{Force, Options};
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::statements::info::InfoStructure;
use crate::sql::{
changefeed::ChangeFeed,
fmt::{is_pretty, pretty_indent},
statements::UpdateStatement,
Base, Ident, Object, Permissions, Strand, Value, Values, View,
Base, Ident, Permissions, Strand, Value, Values, View,
};
use std::sync::Arc;
use crate::sql::statements::info::InfoStructure;
use crate::sql::{Idiom, Kind, Part, Table, TableType};
use crate::sql::{Idiom, Kind, Part, TableType};
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Write};
use super::DefineFieldStatement;
use std::sync::Arc;
#[revisioned(revision = 3)]
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
@ -148,23 +146,20 @@ impl DefineTableStatement {
}
impl DefineTableStatement {
/// Checks if this is a TYPE RELATION table
pub fn is_relation(&self) -> bool {
matches!(self.kind, TableType::Relation(_))
}
/// Checks if this table allows graph edges / relations
pub fn allows_relation(&self) -> bool {
matches!(self.kind, TableType::Relation(_) | TableType::Any)
}
/// Checks if this table allows normal records / documents
pub fn allows_normal(&self) -> bool {
matches!(self.kind, TableType::Normal | TableType::Any)
}
}
fn get_tables_from_kind(tables: &[Table]) -> String {
tables.iter().map(|t| t.0.as_str()).collect::<Vec<_>>().join(" | ")
}
impl Display for DefineTableStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DEFINE TABLE")?;
@ -180,10 +175,18 @@ impl Display for DefineTableStatement {
TableType::Relation(rel) => {
f.write_str(" RELATION")?;
if let Some(Kind::Record(kind)) = &rel.from {
write!(f, " IN {}", get_tables_from_kind(kind))?;
write!(
f,
" IN {}",
kind.iter().map(|t| t.0.as_str()).collect::<Vec<_>>().join(" | ")
)?;
}
if let Some(Kind::Record(kind)) = &rel.to {
write!(f, " OUT {}", get_tables_from_kind(kind))?;
write!(
f,
" OUT {}",
kind.iter().map(|t| t.0.as_str()).collect::<Vec<_>>().join(" | ")
)?;
}
}
TableType::Any => {
@ -220,40 +223,15 @@ impl Display for DefineTableStatement {
impl InfoStructure for DefineTableStatement {
fn structure(self) -> Value {
let Self {
name,
drop,
full,
view,
permissions,
changefeed,
comment,
kind,
..
} = self;
let mut acc = Object::default();
acc.insert("name".to_string(), name.structure());
acc.insert("drop".to_string(), drop.into());
acc.insert("full".to_string(), full.into());
if let Some(view) = view {
acc.insert("view".to_string(), view.structure());
}
acc.insert("permissions".to_string(), permissions.structure());
if let Some(changefeed) = changefeed {
acc.insert("changefeed".to_string(), changefeed.structure());
}
if let Some(comment) = comment {
acc.insert("comment".to_string(), comment.into());
}
acc.insert("kind".to_string(), kind.structure());
Value::Object(acc)
Value::from(map! {
"name".to_string() => self.name.structure(),
"drop".to_string() => self.drop.into(),
"full".to_string() => self.full.into(),
"kind".to_string() => self.kind.structure(),
"view".to_string(), if let Some(v) = self.view => v.structure(),
"changefeed".to_string(), if let Some(v) = self.changefeed => v.structure(),
"permissions".to_string() => self.permissions.structure(),
"comment".to_string(), if let Some(v) = self.comment => v.into(),
})
}
}

View file

@ -5,7 +5,7 @@ use crate::err::Error;
use crate::iam::{Action, ResourceKind};
use crate::sql::statements::info::InfoStructure;
use crate::sql::{
escape::quote_str, fmt::Fmt, user::UserDuration, Base, Duration, Ident, Object, Strand, Value,
escape::quote_str, fmt::Fmt, user::UserDuration, Base, Duration, Ident, Strand, Value,
};
use argon2::{
password_hash::{PasswordHasher, SaltString},
@ -251,37 +251,16 @@ impl Display for DefineUserStatement {
impl InfoStructure for DefineUserStatement {
fn structure(self) -> Value {
let Self {
name,
base,
hash,
roles,
duration,
comment,
..
} = self;
let mut acc = Object::default();
acc.insert("name".to_string(), name.structure());
acc.insert("base".to_string(), base.structure());
acc.insert("passhash".to_string(), hash.into());
acc.insert(
"roles".to_string(),
Value::Array(roles.into_iter().map(|r| r.structure()).collect()),
);
let mut dur = Object::default();
dur.insert("token".to_string(), duration.token.into());
dur.insert("session".to_string(), duration.session.into());
acc.insert("duration".to_string(), dur.to_string().into());
if let Some(comment) = comment {
acc.insert("comment".to_string(), comment.into());
}
Value::Object(acc)
Value::from(map! {
"name".to_string() => self.name.structure(),
"base".to_string() => self.base.structure(),
"hash".to_string() => self.hash.into(),
"roles".to_string() => self.roles.into_iter().map(Ident::structure).collect(),
"duration".to_string() => Value::from(map! {
"token".to_string() => self.duration.token.into(),
"session".to_string() => self.duration.session.into(),
}),
"comment".to_string(), if let Some(v) = self.comment => v.into(),
})
}
}

View file

@ -6,7 +6,7 @@ use crate::fflags::FFLAGS;
use crate::iam::Auth;
use crate::kvs::lq_structs::{LqEntry, TrackedResult};
use crate::sql::statements::info::InfoStructure;
use crate::sql::{Cond, Fetchs, Fields, Object, Table, Uuid, Value};
use crate::sql::{Cond, Fetchs, Fields, Table, Uuid, Value};
use derive::Store;
use futures::lock::MutexGuard;
use reblessive::tree::Stk;
@ -202,27 +202,11 @@ impl fmt::Display for LiveStatement {
impl InfoStructure for LiveStatement {
fn structure(self) -> Value {
let Self {
expr,
what,
cond,
fetch,
..
} = self;
let mut acc = Object::default();
acc.insert("expr".to_string(), expr.structure());
acc.insert("what".to_string(), what.structure());
if let Some(cond) = cond {
acc.insert("cond".to_string(), cond.structure());
}
if let Some(fetch) = fetch {
acc.insert("fetch".to_string(), fetch.structure());
}
Value::Object(acc)
Value::from(map! {
"expr".to_string() => self.expr.structure(),
"what".to_string() => self.what.structure(),
"cond".to_string(), if let Some(v) = self.cond => v.structure(),
"fetch".to_string(), if let Some(v) = self.fetch => v.structure(),
})
}
}

View file

@ -1,12 +1,10 @@
use crate::sql::statements::info::InfoStructure;
use crate::sql::Array;
use crate::sql::{Kind, Value};
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::fmt::Display;
use super::{Kind, Object, Table, Value};
/// The type of records stored by a table
#[revisioned(revision = 1)]
#[derive(Debug, Default, Serialize, Deserialize, Hash, Clone, Eq, PartialEq, PartialOrd)]
@ -27,11 +25,11 @@ impl Display for TableType {
}
TableType::Relation(rel) => {
f.write_str(" RELATION")?;
if let Some(Kind::Record(kind)) = &rel.from {
write!(f, " IN {}", get_tables_from_kind(kind).join(" | "))?;
if let Some(kind) = &rel.from {
write!(f, " IN {kind}")?;
}
if let Some(Kind::Record(kind)) = &rel.to {
write!(f, " OUT {}", get_tables_from_kind(kind).join(" | "))?;
if let Some(kind) = &rel.to {
write!(f, " OUT {kind}")?;
}
}
TableType::Any => {
@ -44,42 +42,24 @@ impl Display for TableType {
impl InfoStructure for TableType {
fn structure(self) -> Value {
let mut acc = Object::default();
match &self {
TableType::Any => {
acc.insert("kind".to_string(), "ANY".into());
}
TableType::Normal => {
acc.insert("kind".to_string(), "NORMAL".into());
}
TableType::Relation(rel) => {
acc.insert("kind".to_string(), "RELATION".into());
if let Some(Kind::Record(tables)) = &rel.from {
acc.insert(
"in".to_string(),
Value::Array(Array::from(get_tables_from_kind(tables))),
);
}
if let Some(Kind::Record(tables)) = &rel.to {
acc.insert(
"out".to_string(),
Value::Array(Array::from(get_tables_from_kind(tables))),
);
}
}
};
Value::Object(acc)
match self {
TableType::Any => Value::from(map! {
"kind".to_string() => "ANY".into(),
}),
TableType::Normal => Value::from(map! {
"kind".to_string() => "NORMAL".into(),
}),
TableType::Relation(rel) => Value::from(map! {
"kind".to_string() => "RELATION".into(),
"in".to_string(), if let Some(Kind::Record(tables)) = rel.from =>
tables.into_iter().map(|t| t.0).collect::<Vec<_>>().into(),
"out".to_string(), if let Some(Kind::Record(tables)) = rel.to =>
tables.into_iter().map(|t| t.0).collect::<Vec<_>>().into(),
}),
}
}
}
fn get_tables_from_kind(tables: &[Table]) -> Vec<&str> {
tables.iter().map(|t| t.0.as_str()).collect::<Vec<_>>()
}
#[revisioned(revision = 1)]
#[derive(Debug, Default, Serialize, Deserialize, Hash, Clone, Eq, PartialEq, PartialOrd)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]

View file

@ -58,6 +58,12 @@ impl Display for Values {
}
}
impl InfoStructure for Values {
fn structure(self) -> Value {
self.into_iter().map(Value::structure).collect::<Vec<_>>().into()
}
}
impl From<&Tables> for Values {
fn from(tables: &Tables) -> Self {
Self(tables.0.iter().map(|t| Value::Table(t.clone())).collect())

View file

@ -577,7 +577,7 @@ async fn access_info_redacted_structure() {
assert!(out.is_ok(), "Unexpected error: {:?}", out);
let out_expected =
r#"{ accesses: [{ base: 'NAMESPACE', duration: '{ session: 6h, token: 15m }', kind: { jwt: { alg: 'HS512', issuer: "{ alg: 'HS512', key: '[REDACTED]' }", key: '[REDACTED]' }, kind: 'JWT' }, name: 'access' }], databases: [], users: [] }"#.to_string();
r#"{ accesses: [{ base: 'NAMESPACE', duration: { session: 6h, token: 15m }, kind: { jwt: { issuer: { alg: 'HS512', key: '[REDACTED]' }, verify: { alg: 'HS512', key: '[REDACTED]' } }, kind: 'JWT' }, name: 'access' }], databases: [], users: [] }"#.to_string();
let out_str = out.unwrap().to_string();
assert_eq!(
out_str, out_expected,
@ -600,7 +600,7 @@ async fn access_info_redacted_structure() {
assert!(out.is_ok(), "Unexpected error: {:?}", out);
let out_expected =
r#"{ accesses: [{ base: 'NAMESPACE', duration: '{ session: 6h, token: 15m }', kind: { jwt: { alg: 'PS512', issuer: "{ alg: 'PS512', key: '[REDACTED]' }", key: 'public' }, kind: 'JWT' }, name: 'access' }], databases: [], users: [] }"#.to_string();
r#"{ accesses: [{ base: 'NAMESPACE', duration: { session: 6h, token: 15m }, kind: { jwt: { issuer: { alg: 'PS512', key: '[REDACTED]' }, verify: { alg: 'PS512', key: 'public' } }, kind: 'JWT' }, name: 'access' }], databases: [], users: [] }"#.to_string();
let out_str = out.unwrap().to_string();
assert_eq!(
out_str, out_expected,
@ -623,7 +623,7 @@ async fn access_info_redacted_structure() {
assert!(out.is_ok(), "Unexpected error: {:?}", out);
let out_expected =
r#"{ accesses: [{ base: 'NAMESPACE', duration: '{ session: 6h, token: 15m }', kind: { jwt: { alg: 'HS512', issuer: "{ alg: 'HS512', key: '[REDACTED]' }", key: '[REDACTED]' }, kind: 'RECORD' }, name: 'access' }], databases: [], users: [] }"#.to_string();
r#"{ accesses: [{ base: 'NAMESPACE', duration: { session: 6h, token: 15m }, kind: { jwt: { issuer: { alg: 'HS512', key: '[REDACTED]' }, verify: { alg: 'HS512', key: '[REDACTED]' } }, kind: 'RECORD' }, name: 'access' }], databases: [], users: [] }"#.to_string();
let out_str = out.unwrap().to_string();
assert_eq!(
out_str, out_expected,