Pull request

use "json"
use "promises"
use req = "request"
use ut = "uri/template"

type PullRequestOrError is (PullRequest | req.RequestError)

class val PullRequest
  """
  A GitHub pull request. Provides a convenience method to fetch the files
  changed in this pull request.
  """
  let _creds: req.Credentials
  let number: I64
  let title: String
  let body: (String | None)
  let state: String
  let labels: Array[Label] val
  let base: PullRequestBase
  let url: String
  let html_url: String
  let files_url: String
  let comments_url: String

  new val create(
    creds: req.Credentials,
    number': I64,
    title': String,
    body': (String | None),
    state': String,
    labels': Array[Label] val,
    base': PullRequestBase,
    url': String,
    html_url': String,
    comments_url': String)
  =>
    _creds = creds
    number = number'
    title = title'
    body = body'
    state = state'
    labels = labels'
    base = base'
    url = url'
    html_url = html_url'
    comments_url = comments_url'
    files_url = url + "/files"

  fun get_files(): Promise[PullRequestFilesOrError] =>
    """
    Fetches the files changed in this pull request.
    """
    GetPullRequestFiles.by_url(files_url, _creds)

primitive GetPullRequest
  """
  Fetches a single pull request by owner, repo, and number.
  """
  fun apply(
    owner: String,
    repo: String,
    number: I64,
    creds: req.Credentials): Promise[PullRequestOrError]
  =>
    match \exhaustive\ ut.URITemplateParse(
      "https://api.github.com/repos" +
      "{/owner}{/repo}/pulls{/number}")
    | let tpl: ut.URITemplate =>
      let vars = ut.URITemplateVariables
        .> set("owner", owner)
        .> set("repo", repo)
        .> set("number", number.string())
      let u: String val = tpl.expand(vars)
      by_url(u, creds)
    | let e: ut.URITemplateParseError =>
      Promise[PullRequestOrError] .> apply(
        req.RequestError(
          where message' = e.message))
    end

  fun by_url(
    url: String,
    creds: req.Credentials
  ): Promise[PullRequestOrError]
  =>
    """
    Fetches a pull request from a URL.
    """
    let p = Promise[PullRequestOrError]
    let r =
      req.ResultReceiver[PullRequest](
        creds,
        p,
        PullRequestJSONConverter)

    req.JSONRequester.get(creds, url, r)
    p

primitive PullRequestJSONConverter is
  req.JSONConverter[PullRequest]
  """
  Converts a JSON object from the pulls API into a PullRequest.
  """
  fun apply(
    json: JSONNav,
    creds: req.Credentials): PullRequest ?
  =>
    """
    Builds a PullRequest from its JSON representation.
    """
    let number = json("number").as_i64()?
    let title = json("title").as_string()?
    let body =
      JSONNavUtil.string_or_none(json("body"))?
    let state = json("state").as_string()?

    let labels = recover trn Array[Label] end
    for i in json("labels").as_array()?.values() do
      let l =
        LabelJSONConverter(JSONNav(i), creds)?
      labels.push(l)
    end

    let base =
      PullRequestBaseJSONConverter(
        json("base"), creds)?

    let url = json("url").as_string()?
    let html_url = json("html_url").as_string()?
    let comments_url =
      json("comments_url").as_string()?

    PullRequest(
      creds,
      number,
      title,
      body,
      state,
      consume labels,
      base,
      url,
      html_url,
      comments_url)