Skip to content

Instantly share code, notes, and snippets.

@titouancreach
Last active April 10, 2025 16:27
Show Gist options
  • Save titouancreach/8bf7ef004d03c6e560e30de75c81d77f to your computer and use it in GitHub Desktop.
Save titouancreach/8bf7ef004d03c6e560e30de75c81d77f to your computer and use it in GitHub Desktop.
import { Effect, Function } from "effect";
import { Pipeable, pipeArguments } from "effect/Pipeable";
import { Predicate, Refinement } from "effect/Predicate";
import { Profil, UnAuthorized } from "../Profil";
import { UserSessionService } from "../UserSession/UserSessionServiceTag";
export interface Policy<A, E = never, R = never> extends Pipeable {
run: (a: A) => Effect.Effect<boolean, E, R>;
readonly _tag: "Policy";
}
export const run =
(msg: string) =>
<E, R>(policy: Policy<Profil, E, R>) =>
Effect.gen(function* () {
const user = yield* UserSessionService.pipe(Effect.flatten);
yield* Effect.if(policy.run(user.profil), {
onTrue: () => Effect.void,
onFalse: () => Effect.fail(new UnAuthorized({ message: msg })),
});
});
export const make = <A = Profil, E = never, R = never>(
run: (a: A) => Effect.Effect<boolean, E, R>,
): Policy<A, E, R> => ({
run,
pipe() {
// biome-ignore lint/style/noArguments: <explanation>
return pipeArguments(this, arguments);
},
_tag: "Policy",
});
export const fromPredicate = <A, E = never, R = never>(
predicate: Predicate<A>,
): Policy<A, E, R> => make((a) => Effect.succeed(predicate(a)));
export const liftPredicate: {
<A, B extends A, E = never, R = never>(
refine: Refinement<A, B>,
): (self: Policy<B, E, R>) => Policy<A, E, R>;
<A, B extends A, E = never, R = never>(
self: Policy<B, E, R>,
refine: Refinement<A, B>,
): Policy<A, E, R>;
} = Function.dual(
2,
<A, B extends A, E = never, R = never>(
self: Policy<B, E, R>,
refine: Refinement<A, B>,
) => ({
run: (a: A) => (refine(a) ? self.run(a) : Effect.succeed(false)),
pipe() {
// biome-ignore lint/style/noArguments: <explanation>
return pipeArguments(this, arguments);
},
_tag: "Policy",
}),
);
export const mapInput: {
<A, B, E = never, R = never>(
self: Policy<B, E, R>,
f: (a: A) => B,
): Policy<A, E, R>;
<A, B, E = never, R = never>(
f: (a: A) => B,
): (p: Policy<B, E, R>) => Policy<A, E, R>;
} = Function.dual(
2,
<A, B, E = never, R = never>(self: Policy<B, E, R>, f: (a: A) => B) => ({
run: (a: A) => self.run(f(a)),
pipe() {
// biome-ignore lint/style/noArguments: <explanation>
return pipeArguments(this, arguments);
},
_tag: "Policy",
}),
);
export const mapInputEffect: {
<A, B, E, R>(
self: Policy<B, E, R>,
f: (a: A) => Effect.Effect<B, E, R>,
): Policy<A, E, R>;
<A, B, E, R>(
f: (a: A) => Effect.Effect<B, E, R>,
): (p: Policy<B, E, R>) => Policy<A, E, R>;
} = Function.dual(
2,
<A, B, E, R>(self: Policy<B, E, R>, f: (a: A) => Effect.Effect<B, E, R>) => ({
run: (a: A) => Effect.flatMap(f(a), (b) => self.run(b)),
pipe() {
// biome-ignore lint/style/noArguments: <explanation>
return pipeArguments(this, arguments);
},
_tag: "Policy",
}),
);
/**
* wrap an effect with a policy
*/
export const withPolicy =
<A, E, R, E2, R2>(msg: string, policy: Policy<Profil, E, R>) =>
(self: Effect.Effect<A, E | E2, R | R2>) =>
Effect.zipRight(run(msg)(policy), self);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment