Skip to content

Commit 2b4b639

Browse files
committed
feat: add support for self expression syntax
1 parent 911ea75 commit 2b4b639

3 files changed

Lines changed: 107 additions & 1 deletion

File tree

src/dsl.test.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,85 @@ definition user {}
461461
expect(rightExpr.relationName).toEqual("d");
462462
});
463463

464+
it("parses definition with a self permission", () => {
465+
const schema = `definition foo {
466+
relation viewer: bar
467+
permission view = viewer + self
468+
}`;
469+
const parsed = parseSchema(schema);
470+
471+
expect(parsed?.definitions.length).toEqual(1);
472+
473+
const definition = parsed?.definitions[0];
474+
assert(definition);
475+
assert(definition.kind === "objectDef");
476+
expect(definition.permissions).toHaveLength(1);
477+
478+
const permission = definition.permissions[0];
479+
assert(permission);
480+
expect(permission.name).toEqual("view");
481+
482+
const binExpr = permission.expr;
483+
assert(binExpr.kind === "binary");
484+
expect(binExpr.operator).toEqual("union");
485+
486+
const leftExpr = binExpr.left;
487+
assert(leftExpr.kind === "relationref");
488+
expect(leftExpr.relationName).toEqual("viewer");
489+
490+
const rightExpr = binExpr.right;
491+
assert(rightExpr.kind === "self");
492+
});
493+
494+
it("parses definition with a standalone self permission", () => {
495+
const schema = `definition foo {
496+
permission view = self
497+
}`;
498+
const parsed = parseSchema(schema);
499+
500+
expect(parsed?.definitions.length).toEqual(1);
501+
502+
const definition = parsed?.definitions[0];
503+
assert(definition);
504+
assert(definition.kind === "objectDef");
505+
expect(definition.permissions).toHaveLength(1);
506+
507+
const permission = definition.permissions[0];
508+
assert(permission);
509+
expect(permission.name).toEqual("view");
510+
511+
const selfExpr = permission.expr;
512+
assert(selfExpr.kind === "self");
513+
});
514+
515+
it("parses definition with self in a complex expression", () => {
516+
const schema = `definition foo {
517+
permission first = ((a - b) + self) & d;
518+
}`;
519+
const parsed = parseSchema(schema);
520+
521+
expect(parsed?.definitions.length).toEqual(1);
522+
523+
const definition = parsed?.definitions[0];
524+
assert(definition);
525+
assert(definition.kind === "objectDef");
526+
expect(definition.permissions).toHaveLength(1);
527+
528+
const permission = definition.permissions[0];
529+
assert(permission);
530+
531+
const binExpr = permission.expr;
532+
assert(binExpr.kind === "binary");
533+
expect(binExpr.operator).toEqual("intersection");
534+
535+
const leftExpr = binExpr.left;
536+
assert(leftExpr.kind === "binary");
537+
expect(leftExpr.operator).toEqual("union");
538+
539+
const rightLeftExpr = leftExpr.right;
540+
assert(rightLeftExpr.kind === "self");
541+
});
542+
464543
it("parses definition with multiple permissions", () => {
465544
const schema = `definition foo {
466545
permission first = firstrel

src/dsl.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ export function flatMapExpression<T>(
130130
return nilResult ? [nilResult] : [];
131131
}
132132

133+
case "self": {
134+
const selfResult = walker(expr);
135+
return selfResult ? [selfResult] : [];
136+
}
137+
133138
case "relationref": {
134139
const result = walker(expr);
135140
return result ? [result] : [];
@@ -280,7 +285,8 @@ export type ParsedExpression =
280285
| ParsedRelationRefExpression
281286
| ParsedArrowExpression
282287
| ParsedNamedArrowExpression
283-
| ParsedNilExpression;
288+
| ParsedNilExpression
289+
| ParsedSelfExpression;
284290

285291
export type ParsedArrowExpression = {
286292
kind: "arrow";
@@ -309,6 +315,11 @@ export type ParsedNilExpression = {
309315
range: TextRange;
310316
};
311317

318+
export type ParsedSelfExpression = {
319+
kind: "self";
320+
range: TextRange;
321+
};
322+
312323
export type ParsedBinaryExpression = {
313324
kind: "binary";
314325
operator: "union" | "intersection" | "exclusion";
@@ -582,13 +593,28 @@ const nilExpr: Parser<ParsedNilExpression> = lazy(() => {
582593
);
583594
});
584595

596+
const selfExpr: Parser<ParsedSelfExpression> = lazy(() => {
597+
return seqMap(
598+
index,
599+
string("self"),
600+
index,
601+
function (startIndex, _data, endIndex) {
602+
return {
603+
kind: "self",
604+
range: { startIndex: startIndex, endIndex: endIndex },
605+
};
606+
},
607+
);
608+
});
609+
585610
const parensExpr = lazy(() =>
586611
string("(")
587612
.then(expr)
588613
.skip(string(")"))
589614
.or(arrowExpr)
590615
.or(namedArrowExpr)
591616
.or(nilExpr)
617+
.or(selfExpr)
592618
.or(relationReference),
593619
);
594620

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export type {
99
ParsedBinaryExpression,
1010
ParsedExpression,
1111
ParsedRelationRefExpression,
12+
ParsedSelfExpression,
1213
TypeRef,
1314
} from "./dsl";
1415
export { ResolvedDefinition, Resolver } from "./resolution";

0 commit comments

Comments
 (0)