<commit_before>import asynchttpserver, asyncdispatch, httpcore

type
  List[A] = ref object
    value: A
    next: List[A]
  StrPair* = tuple[k, v: string]
  # TODO: replace these by httpcore-HttpMethod
  Context* = object
    position*: int
    accept*: bool
    log*: ref string
    headers*: List[StrPair]


proc emptyList[A](): List[A] = nil

proc `+`[A](a: A, list: List[A]): List[A] =
  new result
  result.value = a
  result.next = list

iterator items*[A](list: List[A]): A =
  var node = list
  while node != nil:
    yield node.value
    node = node.next

proc reject*(ctx: Context): Context =
  Context(
    position: ctx.position,
    accept: false,
    log: ctx.log,
    headers: ctx.headers
  )

proc addPosition*(ctx: Context, n: int): Context =
  Context(
    position: ctx.position + n,
    accept: ctx.accept,
    log: ctx.log,
    headers: ctx.headers
  )

proc withPosition*(ctx: Context, n: int): Context =
  Context(
    position: n,
    accept: ctx.accept,
    log: ctx.log,
    headers: ctx.headers
  )

proc withLogging*(ctx: Context, s: ref string): Context =
  Context(
    position: ctx.position,
    accept: ctx.accept,
    log: s,
    headers: ctx.headers
  )

proc withHeaders*(ctx: Context, hs: openarray[StrPair]): Context =
  var headers = ctx.headers
  for h in hs:
    headers = h + headers
  return Context(
    position: ctx.position,
    accept: ctx.accept,
    log: ctx.log,
    headers: headers
  )

type Handler* = proc(req: ref Request, ctx: Context): Future[Context]

proc handle*(h: Handler): auto =
  proc server(req: Request): Future[void] {.async, closure.} =
    let emptyCtx = Context(
      position: 0,
      accept: true,
      headers: emptyList[StrPair]()
    )
    var reqHeap = new(Request)
    reqHeap[] = req
    var
      f: Future[Context]
      ctx: Context
    try:
      f = h(reqHeap, emptyCtx)
      ctx = await f
    except:
      discard
    if f.failed:
      await req.respond(Http500, "Server Error", {"Content-Type": "text/plain;charset=utf-8"}.newHttpHeaders)
    else:
      if not ctx.accept:
        await req.respond(Http404, "Not Found", {"Content-Type": "text/plain;charset=utf-8"}.newHttpHeaders)

  return server

proc `~`*(h1, h2: Handler): Handler =
  proc h3(req: ref Request, ctx: Context): Future[Context] {.async.} =
    let newCtx = await h1(req, ctx)
    if newCtx.accept:
      return newCtx
    else:
      return await h2(req, ctx)

  return h3

proc `->`*(h1, h2: Handler): Handler =
  proc h3(req: ref Request, ctx: Context): Future[Context] {.async.} =
    let newCtx = await h1(req, ctx)
    if newCtx.accept:
      return await h2(req, newCtx)
    else:
      return newCtx

  return h3

template `[]`*(h1, h2: Handler): auto = h1 -> h2

proc serve*(server: AsyncHttpServer, port: Port, handler: Handler, address = ""): Future[void] =
  echo "My most dear lord!"
  echo "Rosencrantz ready on port ", port.int16
  serve(server, port, handle(handler), address)<commit_msg>make it compile on the latest Nim devel<commit_after>import asynchttpserver, asyncdispatch, httpcore

type
  List[A] = ref object
    value: A
    next: List[A]
  StrPair* = tuple[k, v: string]
  # TODO: replace these by httpcore-HttpMethod
  Context* = object
    position*: int
    accept*: bool
    log*: ref string
    headers*: List[StrPair]


proc emptyList[A](): List[A] = nil

proc `+`[A](a: A, list: List[A]): List[A] =
  new result
  result.value = a
  result.next = list

iterator items*[A](list: List[A]): A =
  var node = list
  while node != nil:
    yield node.value
    node = node.next

proc reject*(ctx: Context): Context =
  Context(
    position: ctx.position,
    accept: false,
    log: ctx.log,
    headers: ctx.headers
  )

proc addPosition*(ctx: Context, n: int): Context =
  Context(
    position: ctx.position + n,
    accept: ctx.accept,
    log: ctx.log,
    headers: ctx.headers
  )

proc withPosition*(ctx: Context, n: int): Context =
  Context(
    position: n,
    accept: ctx.accept,
    log: ctx.log,
    headers: ctx.headers
  )

proc withLogging*(ctx: Context, s: ref string): Context =
  Context(
    position: ctx.position,
    accept: ctx.accept,
    log: s,
    headers: ctx.headers
  )

proc withHeaders*(ctx: Context, hs: openarray[StrPair]): Context =
  var headers = ctx.headers
  for h in hs:
    headers = h + headers
  return Context(
    position: ctx.position,
    accept: ctx.accept,
    log: ctx.log,
    headers: headers
  )

type Handler* = proc(req: ref Request, ctx: Context): Future[Context]

proc handle*(h: Handler): auto {.gcsafe.} =
  proc server(req: Request): Future[void] {.async, closure.} =
    let emptyCtx = Context(
      position: 0,
      accept: true,
      headers: emptyList[StrPair]()
    )
    var reqHeap = new(Request)
    reqHeap[] = req
    var
      f: Future[Context]
      ctx: Context
    try:
      f = h(reqHeap, emptyCtx)
      ctx = await f
    except:
      discard
    if f.failed:
      await req.respond(Http500, "Server Error", {"Content-Type": "text/plain;charset=utf-8"}.newHttpHeaders)
    else:
      if not ctx.accept:
        await req.respond(Http404, "Not Found", {"Content-Type": "text/plain;charset=utf-8"}.newHttpHeaders)

  return server

proc `~`*(h1, h2: Handler): Handler =
  proc h3(req: ref Request, ctx: Context): Future[Context] {.async.} =
    let newCtx = await h1(req, ctx)
    if newCtx.accept:
      return newCtx
    else:
      return await h2(req, ctx)

  return h3

proc `->`*(h1, h2: Handler): Handler =
  proc h3(req: ref Request, ctx: Context): Future[Context] {.async.} =
    let newCtx = await h1(req, ctx)
    if newCtx.accept:
      return await h2(req, newCtx)
    else:
      return newCtx

  return h3

template `[]`*(h1, h2: Handler): auto = h1 -> h2

proc serve*(server: AsyncHttpServer, port: Port, handler: Handler, address = ""): Future[void] =
  echo "My most dear lord!"
  echo "Rosencrantz ready on port ", port.int16
  serve(server, port, handle(handler), address)

{'fraction_non_alphanumeric': 0.1336235766912257, 'fraction_numerical': 0.009042196918955124, 'mean_word_length': 2.997991967871486, 'pattern_counts': {'":': 4, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '618162', 'n_tokens_mistral': 2066, 'n_tokens_neox': 2042, 'n_words': 613}