Skip to content

Instantly share code, notes, and snippets.

@arosien
Created October 11, 2023 17:18
Show Gist options
  • Save arosien/5b912b6b36415d6a146f839baf154af9 to your computer and use it in GitHub Desktop.
Save arosien/5b912b6b36415d6a146f839baf154af9 to your computer and use it in GitHub Desktop.
//> using scala 2
//> using toolkit typelevel:latest
// https://practicalocaml.com/how-to-build-type-safe-state-machines-using-type-state/
// type 'state fsm = { state: 'state }
case class Fsm[State](state: State)
/*
type id
type scope
type 'resource granted = { resource : 'resource }
type denied = { reason : string }
type requested = { scopes : scope list }
*/
case class Id(value: String)
case class Scope(value: String)
case class Granted[Resource](resource: Resource)
case class Denied(reason: String)
case class Requested(scopes: List[Scope])
/*
type ('state, 'resource) t = {
(* the state of the permission request *)
state : 'state;
(* other shared metadata *)
resource_id : id;
user_id : id;
}
*/
case class T[State, Resource](state: State, resourceId: Id, userId: Id) {
/*
let make ~resource_id ~user_id ~scopes =
{ state = { scopes }; resource_id; user_id }
(* TODO(@you): you can implement here the logic for checking
* if you actually have permissions for this request :)
*)
let run_request : (requested, 'resource') t -> ('resource, string) result =
fun _t -> Error "unimplemented!"
let request_access t =
match run_request t with
| Ok resource -> Ok { t with state = { resource } }
| Error reason -> Error { t with state = { reason } }
let reason { state = { reason }; _ } = reason
let with_resource { state = { resource }; _ } fn = fn resource
let get { state = {resource}; _ } = resource
*/
private def run(implicit ev: State =:= Requested): Either[String, Resource] =
Left("Unimplemented")
def requestAccess(implicit
ev: State =:= Requested
): Either[T[Denied, Resource], T[Granted[Resource], Resource]] =
run match {
case Left(reason) => Left(copy(state = Denied(reason)))
case Right(resource) => Right(copy(state = Granted(resource)))
}
def reason(implicit ev: State =:= Denied): String =
ev(this.state).reason
def resource(implicit ev: State =:= Granted[Resource]): Resource =
withResource(identity)
def withResource[B](f: Resource => B)(implicit
ev: State =:= Granted[Resource]
): B =
f(ev(this.state).resource)
}
object T {
def apply[Resource](
resourceId: Id,
userId: Id,
scopes: List[Scope]
): T[Requested, Resource] =
T(Requested(scopes), resourceId, userId)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment