diff --git a/src/dsl.test.ts b/src/dsl.test.ts index 2e2a50d..a5dce72 100644 --- a/src/dsl.test.ts +++ b/src/dsl.test.ts @@ -461,6 +461,85 @@ definition user {} expect(rightExpr.relationName).toEqual("d"); }); + it("parses definition with a self permission", () => { + const schema = `definition foo { + relation viewer: bar + permission view = viewer + self + }`; + const parsed = parseSchema(schema); + + expect(parsed?.definitions.length).toEqual(1); + + const definition = parsed?.definitions[0]; + assert(definition); + assert(definition.kind === "objectDef"); + expect(definition.permissions).toHaveLength(1); + + const permission = definition.permissions[0]; + assert(permission); + expect(permission.name).toEqual("view"); + + const binExpr = permission.expr; + assert(binExpr.kind === "binary"); + expect(binExpr.operator).toEqual("union"); + + const leftExpr = binExpr.left; + assert(leftExpr.kind === "relationref"); + expect(leftExpr.relationName).toEqual("viewer"); + + const rightExpr = binExpr.right; + assert(rightExpr.kind === "self"); + }); + + it("parses definition with a standalone self permission", () => { + const schema = `definition foo { + permission view = self + }`; + const parsed = parseSchema(schema); + + expect(parsed?.definitions.length).toEqual(1); + + const definition = parsed?.definitions[0]; + assert(definition); + assert(definition.kind === "objectDef"); + expect(definition.permissions).toHaveLength(1); + + const permission = definition.permissions[0]; + assert(permission); + expect(permission.name).toEqual("view"); + + const selfExpr = permission.expr; + assert(selfExpr.kind === "self"); + }); + + it("parses definition with self in a complex expression", () => { + const schema = `definition foo { + permission first = ((a - b) + self) & d; + }`; + const parsed = parseSchema(schema); + + expect(parsed?.definitions.length).toEqual(1); + + const definition = parsed?.definitions[0]; + assert(definition); + assert(definition.kind === "objectDef"); + expect(definition.permissions).toHaveLength(1); + + const permission = definition.permissions[0]; + assert(permission); + + const binExpr = permission.expr; + assert(binExpr.kind === "binary"); + expect(binExpr.operator).toEqual("intersection"); + + const leftExpr = binExpr.left; + assert(leftExpr.kind === "binary"); + expect(leftExpr.operator).toEqual("union"); + + const rightLeftExpr = leftExpr.right; + assert(rightLeftExpr.kind === "self"); + }); + it("parses definition with multiple permissions", () => { const schema = `definition foo { permission first = firstrel diff --git a/src/dsl.ts b/src/dsl.ts index 82f153c..671a67d 100644 --- a/src/dsl.ts +++ b/src/dsl.ts @@ -130,6 +130,11 @@ export function flatMapExpression( return nilResult ? [nilResult] : []; } + case "self": { + const selfResult = walker(expr); + return selfResult ? [selfResult] : []; + } + case "relationref": { const result = walker(expr); return result ? [result] : []; @@ -280,7 +285,8 @@ export type ParsedExpression = | ParsedRelationRefExpression | ParsedArrowExpression | ParsedNamedArrowExpression - | ParsedNilExpression; + | ParsedNilExpression + | ParsedSelfExpression; export type ParsedArrowExpression = { kind: "arrow"; @@ -309,6 +315,11 @@ export type ParsedNilExpression = { range: TextRange; }; +export type ParsedSelfExpression = { + kind: "self"; + range: TextRange; +}; + export type ParsedBinaryExpression = { kind: "binary"; operator: "union" | "intersection" | "exclusion"; @@ -582,6 +593,20 @@ const nilExpr: Parser = lazy(() => { ); }); +const selfExpr: Parser = lazy(() => { + return seqMap( + index, + string("self"), + index, + function (startIndex, _data, endIndex) { + return { + kind: "self", + range: { startIndex: startIndex, endIndex: endIndex }, + }; + }, + ); +}); + const parensExpr = lazy(() => string("(") .then(expr) @@ -589,6 +614,7 @@ const parensExpr = lazy(() => .or(arrowExpr) .or(namedArrowExpr) .or(nilExpr) + .or(selfExpr) .or(relationReference), ); diff --git a/src/index.ts b/src/index.ts index 903faec..c615695 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,7 @@ export type { ParsedBinaryExpression, ParsedExpression, ParsedRelationRefExpression, + ParsedSelfExpression, TypeRef, } from "./dsl"; export { ResolvedDefinition, Resolver } from "./resolution";