surrealpatch/lib/src/sql/permission.rs

277 lines
6.4 KiB
Rust

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::Write;
use std::fmt::{self, Display, Formatter};
use std::str;
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Hash)]
pub struct Permissions {
pub select: Permission,
pub create: Permission,
pub update: Permission,
pub delete: Permission,
}
impl Permissions {
pub fn none() -> Self {
Permissions {
select: Permission::None,
create: Permission::None,
update: Permission::None,
delete: Permission::None,
}
}
pub fn full() -> Self {
Permissions {
select: Permission::Full,
create: Permission::Full,
update: Permission::Full,
delete: Permission::Full,
}
}
pub fn is_none(&self) -> bool {
self.select == Permission::None
&& self.create == Permission::None
&& self.update == Permission::None
&& self.delete == Permission::None
}
pub fn is_full(&self) -> bool {
self.select == Permission::Full
&& self.create == Permission::Full
&& self.update == Permission::Full
&& self.delete == Permission::Full
}
}
impl Display for Permissions {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "PERMISSIONS")?;
if self.is_none() {
return write!(f, " NONE");
}
if self.is_full() {
return write!(f, " FULL");
}
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(())
}
}
pub fn permissions(i: &str) -> IResult<&str, Permissions> {
let (i, _) = tag_no_case("PERMISSIONS")(i)?;
let (i, _) = shouldbespace(i)?;
alt((none, full, specific))(i)
}
fn none(i: &str) -> IResult<&str, Permissions> {
let (i, _) = tag_no_case("NONE")(i)?;
Ok((i, Permissions::none()))
}
fn full(i: &str) -> IResult<&str, Permissions> {
let (i, _) = tag_no_case("FULL")(i)?;
Ok((i, Permissions::full()))
}
fn specific(i: &str) -> IResult<&str, Permissions> {
let (i, perms) = separated_list0(commasorspace, permission)(i)?;
Ok((
i,
Permissions {
select: perms
.iter()
.find_map(|x| {
x.iter().find_map(|y| match y {
('s', ref v) => Some(v.to_owned()),
_ => None,
})
})
.unwrap_or_default(),
create: perms
.iter()
.find_map(|x| {
x.iter().find_map(|y| match y {
('c', ref v) => Some(v.to_owned()),
_ => None,
})
})
.unwrap_or_default(),
update: perms
.iter()
.find_map(|x| {
x.iter().find_map(|y| match y {
('u', ref v) => Some(v.to_owned()),
_ => None,
})
})
.unwrap_or_default(),
delete: perms
.iter()
.find_map(|x| {
x.iter().find_map(|y| match y {
('d', ref v) => Some(v.to_owned()),
_ => None,
})
})
.unwrap_or_default(),
},
))
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
pub enum Permission {
None,
Full,
Specific(Value),
}
impl Default for Permission {
fn default() -> Self {
Self::Full
}
}
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"),
Self::Specific(ref v) => write!(f, "WHERE {}", v),
}
}
}
fn permission(i: &str) -> IResult<&str, Vec<(char, Permission)>> {
let (i, _) = tag_no_case("FOR")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, kind) = separated_list0(
commas,
alt((
map(tag_no_case("SELECT"), |_| 's'),
map(tag_no_case("CREATE"), |_| 'c'),
map(tag_no_case("UPDATE"), |_| 'u'),
map(tag_no_case("DELETE"), |_| 'd'),
)),
)(i)?;
let (i, _) = shouldbespace(i)?;
let (i, expr) = alt((
map(tag_no_case("NONE"), |_| Permission::None),
map(tag_no_case("FULL"), |_| Permission::Full),
map(tuple((tag_no_case("WHERE"), shouldbespace, value)), |(_, _, v)| {
Permission::Specific(v)
}),
))(i)?;
Ok((i, kind.into_iter().map(|k| (k, expr.clone())).collect()))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sql::expression::Expression;
use crate::sql::test::Parse;
#[test]
fn permissions_none() {
let sql = "PERMISSIONS NONE";
let res = permissions(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("PERMISSIONS NONE", format!("{}", out));
assert_eq!(out, Permissions::none());
}
#[test]
fn permissions_full() {
let sql = "PERMISSIONS FULL";
let res = permissions(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("PERMISSIONS FULL", format!("{}", out));
assert_eq!(out, Permissions::full());
}
#[test]
fn permissions_specific() {
let sql =
"PERMISSIONS FOR select FULL, FOR create, update WHERE public = true, FOR delete NONE";
let res = permissions(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(
"PERMISSIONS FOR select FULL, FOR create, update WHERE public = true, FOR delete NONE",
format!("{}", out)
);
assert_eq!(
out,
Permissions {
select: Permission::Full,
create: Permission::Specific(Value::from(Expression::parse("public = true"))),
update: Permission::Specific(Value::from(Expression::parse("public = true"))),
delete: Permission::None,
}
);
}
}