///|
fn render_guid(w : XmlWriter, item : FeedItem) -> Unit {
  let (guid_value, is_perma_link) = match
    (item.guid, item.url, item.guid_is_perma_link) {
    (Some(g), _, Some(pl)) => (g, pl)
    (Some(g), _, None) => (g, true)
    (None, Some(u), _) => (u, true)
    (None, None, _) => (item.title, false)
  }
  let is_pl_str = if is_perma_link { "true" } else { "false" }
  w.leaf_with_attrs("guid", [("isPermaLink", is_pl_str)], guid_value)
}

///|
fn render_feed_enclosure(
  errors : Array[FeedError],
  w : XmlWriter,
  enclosure : FeedEnclosure,
) -> Unit {
  enclosure.validate(errors)
  let mime = enclosure.effective_mime()
  w.self_closing("enclosure", [
    ("url", enclosure.url),
    ("length", enclosure.size.to_string()),
    ("type", mime),
  ])
}

///|
fn feed_item_xml(
  errors : Array[FeedError],
  w : XmlWriter,
  item : FeedItem,
  channel_author : Author?,
) -> Unit {
  w.open("item", [])
  w.leaf_cdata("title", item.title)
  w.opt_leaf_cdata("description", item.description)
  w.opt_leaf("link", item.url)
  match item.content {
    Some(c) => w.leaf_cdata("content:encoded", c)
    None => ()
  }
  render_guid(w, item)
  for category in item.categories {
    feed_category_xml(w, category)
  }
  match (item.author, channel_author) {
    (Some(a), _) => w.leaf_cdata("dc:creator", a.name)
    (None, Some(a)) => w.leaf_cdata("dc:creator", a.name)
    (None, None) => ()
  }
  w.opt_leaf("pubDate", optional_date_to_rfc822(item.pub_date))
  match item.lat {
    Some(lat) =>
      if validate_geo_value(lat, -90.0, 90.0, "latitude", errors) {
        w.leaf("geo:lat", format_geo(lat))
      }
    None => ()
  }
  match item.long {
    Some(lon) =>
      if validate_geo_value(lon, -180.0, 180.0, "longitude", errors) {
        w.leaf("geo:long", format_geo(lon))
      }
    None => ()
  }
  match item.enclosure {
    Some(enclosure) => render_feed_enclosure(errors, w, enclosure)
    None => ()
  }
  for elem in item.custom_elements {
    custom_elem_xml(errors, w, elem)
  }
  w.close("item")
}

///|
fn feed_category_xml(w : XmlWriter, category : FeedCategory) -> Unit {
  match category.domain {
    Some(d) =>
      w.leaf_cdata_with_attrs("category", [("domain", d)], category.value)
    None => w.leaf_cdata("category", category.value)
  }
}