surrealdb-sql 1.1.0

Full type definitions for the SurrealQL query language
Documentation
use super::super::{
	comment::shouldbespace,
	common::{commas, commasorspace},
	error::expected,
	value::value,
	IResult,
};
use crate::{
	permission::{Permission, PermissionKind},
	Permissions,
};
use nom::{
	branch::alt,
	bytes::complete::tag_no_case,
	combinator::{self, cut, map},
	multi::{separated_list0, separated_list1},
	sequence::tuple,
};

pub fn permissions(i: &str, default: Permission) -> IResult<&str, Permissions> {
	let (i, _) = tag_no_case("PERMISSIONS")(i)?;
	let (i, _) = shouldbespace(i)?;
	cut(alt((none, full, specific(default))))(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(default: Permission) -> impl Fn(&str) -> IResult<&str, Permissions> {
	move |i: &str| -> IResult<&str, Permissions> {
		let (i, perms) = separated_list1(commasorspace, rule)(i)?;
		Ok((
			i,
			Permissions {
				select: perms
					.iter()
					.find_map(|x| {
						x.iter().find_map(|y| match y {
							(PermissionKind::Select, ref v) => Some(v.to_owned()),
							_ => None,
						})
					})
					.unwrap_or(default.to_owned()),
				create: perms
					.iter()
					.find_map(|x| {
						x.iter().find_map(|y| match y {
							(PermissionKind::Create, ref v) => Some(v.to_owned()),
							_ => None,
						})
					})
					.unwrap_or(default.to_owned()),
				update: perms
					.iter()
					.find_map(|x| {
						x.iter().find_map(|y| match y {
							(PermissionKind::Update, ref v) => Some(v.to_owned()),
							_ => None,
						})
					})
					.unwrap_or(default.to_owned()),
				delete: perms
					.iter()
					.find_map(|x| {
						x.iter().find_map(|y| match y {
							(PermissionKind::Delete, ref v) => Some(v.to_owned()),
							_ => None,
						})
					})
					.unwrap_or(default.to_owned()),
			},
		))
	}
}

pub fn permission(i: &str) -> IResult<&str, Permission> {
	expected(
		"a permission",
		alt((
			combinator::value(Permission::None, tag_no_case("NONE")),
			combinator::value(Permission::Full, tag_no_case("FULL")),
			map(tuple((tag_no_case("WHERE"), shouldbespace, value)), |(_, _, v)| {
				Permission::Specific(v)
			}),
		)),
	)(i)
}

fn rule(i: &str) -> IResult<&str, Vec<(PermissionKind, Permission)>> {
	let (i, _) = tag_no_case("FOR")(i)?;
	let (i, _) = shouldbespace(i)?;
	cut(|i| {
		let (i, kind) = separated_list0(
			commas,
			alt((
				combinator::value(PermissionKind::Select, tag_no_case("SELECT")),
				combinator::value(PermissionKind::Create, tag_no_case("CREATE")),
				combinator::value(PermissionKind::Update, tag_no_case("UPDATE")),
				combinator::value(PermissionKind::Delete, tag_no_case("DELETE")),
			)),
		)(i)?;
		let (i, _) = shouldbespace(i)?;
		let (i, expr) = alt((
			combinator::value(Permission::None, tag_no_case("NONE")),
			combinator::value(Permission::Full, tag_no_case("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()))
	})(i)
}

#[cfg(test)]
mod test {
	use crate::syn::test::Parse;
	use crate::{Expression, Value};

	use super::*;

	#[test]
	fn permissions_none() {
		let sql = "PERMISSIONS NONE";
		let res = permissions(sql, Permission::Full);
		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, Permission::None);
		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, Permission::None);
		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,
			}
		);
	}

	#[test]
	fn no_empty_permissions() {
		// This was previouslly allowed,
		let sql = "PERMISSION ";
		permission(sql).unwrap_err();
	}
}