com-lihaoyi / cask

Cask: a Scala HTTP micro-framework. Cask makes it easy to set up a website, backend server, or REST API using Scala
https://com-lihaoyi.github.io/cask/
Other
525 stars 55 forks source link

[RFC] Parameterize context type of decorators #137

Open jodersky opened 1 month ago

jodersky commented 1 month ago

This allows customizing the way by-type parameters can be read: instead of always reading data from a cask.Request, this allows a decorator to parameterize the context type and pass it in explicitly from the wrapFunction method.

Essentially, this makes the context parameter of ArgReaders (which are used to translate data from a HTTP request to a scala parameter) customizable for every decorator.

Motivation

The idea behind this proposal is to uniformize the way "named" and "by-type" parameters within the same endpoint are handled. By "named" parameters I mean parameters which are set from the Map[String, Input] in the delegate function, and by by-type parameters I mean parameters which are computed from an arity zero ArgReader, and thus use the cask.Request context to compute their value.

E.g.

@cask.get(/:foo/:bar)
def index(foo: String, bar: Int, cookie1: cask.Cookies, req: cask.Request)

In this case, foo and bar and "named" and cookie1 and req are "by-type".

Right now, the wrapFunction method handles how named parameters are set, and therefore can centrally do arbitrary pre- and post-processing. However, by-type parameters must always use a cask.Request to compute any values, which can be problematic if the computation is expensive or if any kind of state is maintained.

This pull request removes this asymmetry, by allowing the wrapFunction to compute a custom context which is computed once before being passed to all InputReaders

Implementation approach

This is a proof of concept PR. As of this writing, the code only works for Scala 3.

lihaoyi commented 1 month ago

I think this looks reasonable