Skip to main content

src/gdav/list_calendars.gleam

import gdav.{type Credentials}
import gdav/internal
import gdav/internal/xml
import gleam/http
import gleam/http/request.{type Request}
import gleam/http/response.{type Response}
import gleam/list
import gleam/result

pub type Calendar {
  Calendar(href: String, displayname: String, ctag: String)
}

pub type RequestBuilder {
  RequestBuilder(calendar_home_path: String)
}

pub fn request(calendar_home_path: String) -> RequestBuilder {
  RequestBuilder(calendar_home_path:)
}

pub fn build(
  builder: RequestBuilder,
  credentials: Credentials,
) -> Request(String) {
  let headers = [
    #("Depth", "1"),
    #("Prefer", "return-minimal"),
    #("Content-Type", "application/xml; charset=utf-8"),
  ]
  let body =
    "<d:propfind xmlns:d=\"DAV:\" xmlns:cs=\"http://calendarserver.org/ns/\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\">"
    <> "<d:prop>"
    <> "<d:resourcetype />"
    <> "<d:displayname />"
    <> "<cs:getctag />"
    <> "<c:supported-calendar-component-set />"
    <> "</d:prop>"
    <> "</d:propfind>"

  internal.request(
    credentials,
    http.Other("PROPFIND"),
    builder.calendar_home_path,
    headers,
    body,
  )
}

pub fn response(res: Response(String)) -> Result(List(Calendar), gdav.DavError) {
  case res.status {
    s if s >= 200 && s < 300 -> {
      use responses <- result.try(xml.parse_multistatus(res.body))
      let calendars =
        list.filter_map(responses, fn(r) {
          let find = fn(ns, local) { xml.find_text(r.properties, ns, local) }
          case find(xml.ns_dav, "displayname"), find(xml.ns_cs, "getctag") {
            Ok(displayname), Ok(ctag) ->
              Ok(Calendar(href: r.href, displayname:, ctag:))
            _, _ -> Error(Nil)
          }
        })
      Ok(calendars)
    }
    401 | 403 -> Error(gdav.AuthenticationFailed)
    _ -> Error(gdav.UnexpectedResponse(res))
  }
}