///|
/// Represents a category in an RSS feed.
pub(all) struct Category {
  /// The domain of the category (a URL identifying its scheme).
  domain : String?
  /// The name of the category.
  name : String
} derive(Debug, Eq)

///|
/// Represents a cloud in an RSS feed: a registry for update notifications.
pub(all) struct Cloud {
  /// The domain to contact.
  domain : String
  /// The port to contact.
  port : String
  /// The path to the notification endpoint.
  path : String
  /// The procedure to call.
  register_procedure : String
  /// The protocol used, e.g. `"xml-rpc"`.
  protocol : String
} derive(Debug, Eq)

///|
/// Represents an enclosure (media attachment) in an RSS item.
pub(all) struct Enclosure {
  /// The URL of the enclosure.
  url : String
  /// Its size in bytes.
  length : String
  /// Its MIME type.
  mime_type : String
} derive(Debug, Eq)

///|
/// Represents the GUID of an RSS item.
pub(all) struct Guid {
  /// Whether the GUID is a permalink to the item. Defaults to `true`.
  is_permalink : Bool
  /// The GUID value.
  value : String
} derive(Debug, Eq)

///|
/// Represents an image that can be displayed with a channel.
pub(all) struct Image {
  /// The URL of the image.
  url : String
  /// A caption for the image.
  title : String
  /// The link the image points to.
  link : String
  /// The width of the image in pixels.
  width : String?
  /// The height of the image in pixels.
  height : String?
  /// A description of the image.
  description : String?
} derive(Debug, Eq)

///|
/// Represents the source of an RSS item.
pub(all) struct Source {
  /// The URL of the source feed.
  url : String
  /// The title of the source feed.
  title : String?
} derive(Debug, Eq)

///|
/// Represents a text input that can be displayed with a channel.
pub(all) struct TextInput {
  /// The label of the input button.
  title : String
  /// A description of the text input.
  description : String
  /// The name of the text field.
  name : String
  /// The URL the input submits to.
  link : String
} derive(Debug, Eq)

///|
/// An element from a non-default namespace, kept generically.
pub(all) struct Extension {
  /// The qualified name of the extension element (as seen on the end tag).
  name : String
  /// The concatenated character content, trimmed; absent when empty.
  value : String?
  /// The attributes of the element.
  attrs : Map[String, String]
  /// Child elements keyed by local name.
  children : Map[String, Array[Extension]]
} derive(Debug, Eq)

///|
/// A map of extension namespace prefixes to local names to elements.
///
/// Mirrors `rss::extension::ExtensionMap`.
pub type ExtensionMap = Map[String, Map[String, Array[Extension]]]

///|
/// Record an extension element under `ns` / `name`.
pub fn extension_entry(
  extensions : ExtensionMap,
  ns : String,
  name : String,
) -> Array[Extension] {
  let inner = match extensions.get(ns) {
    Some(inner) => inner
    None => {
      let inner : Map[String, Array[Extension]] = {}
      extensions[ns] = inner
      inner
    }
  }
  let list = match inner.get(name) {
    Some(list) => list
    None => {
      let list : Array[Extension] = []
      inner[name] = list
      list
    }
  }
  list
}

///|
/// Represents an item in an RSS feed.
pub(all) struct Item {
  /// The title of the item.
  title : String?
  /// The URL of the item.
  link : String?
  /// The item synopsis.
  description : String?
  /// The email address of the item's author.
  author : String?
  /// The categories the item belongs to.
  categories : Array[Category]
  /// The URL for the comments page of the item.
  comments : String?
  /// A media object attached to the item.
  enclosure : Enclosure?
  /// A unique identifier for the item.
  guid : Guid?
  /// The publication date as an RFC 2822 timestamp.
  pub_date : String?
  /// The source feed of the item.
  source : Source?
  /// The HTML contents of the item (`content:encoded`).
  content : String?
  /// Extensions stored under their namespace prefix.
  extensions : ExtensionMap
  /// Extracted Atom links (`atom:link` elements).
  atom_links : Array[@atom.Link]
  /// Extracted iTunes podcast metadata.
  itunes_ext : ITunesItemExtension?
  /// Extracted Dublin Core metadata.
  dublin_core_ext : DublinCoreExtension?
} derive(Debug, Eq)

///|
/// Represents the channel of an RSS feed.
pub(all) struct Channel {
  /// The name of the channel. Required by RSS 2.0; defaults to `""` when
  /// parsing invalid feeds.
  title : String
  /// The URL of the website corresponding to the channel.
  link : String
  /// A description of the channel.
  description : String
  /// The language of the channel.
  language : String?
  /// The copyright notice for the channel.
  copyright : String?
  /// The email address of the managing editor.
  managing_editor : String?
  /// The email address of the webmaster.
  webmaster : String?
  /// The publication date as an RFC 822 timestamp.
  pub_date : String?
  /// The last-change date as an RFC 822 timestamp.
  last_build_date : String?
  /// The categories the channel belongs to.
  categories : Array[Category]
  /// The program used to generate the channel.
  generator : String?
  /// A URL pointing to the documentation of the RSS format.
  docs : String?
  /// The cloud registration endpoint for update notifications.
  cloud : Cloud?
  /// The PICS rating of the channel.
  rating : String?
  /// The number of minutes the channel may be cached.
  ttl : String?
  /// An image displayed with the channel.
  image : Image?
  /// A text input displayed with the channel.
  text_input : TextInput?
  /// Hours (0–23) during which aggregators may skip refreshing.
  skip_hours : Array[String]
  /// Days (Monday, Tuesday, …) during which aggregators may skip refreshing.
  skip_days : Array[String]
  /// The items in the channel.
  items : Array[Item]
  /// Generic extensions stored under their namespace prefix.
  extensions : ExtensionMap
  /// Extracted Atom links (`atom:link` elements on the channel).
  atom_links : Array[@atom.Link]
  /// Extracted iTunes podcast metadata.
  itunes_ext : ITunesChannelExtension?
  /// Extracted Dublin Core metadata.
  dublin_core_ext : DublinCoreExtension?
  /// Extracted syndication metadata.
  syndication_ext : SyndicationExtension?
  /// Namespace declarations present on the root tag.
  namespaces : Map[String, String]
} derive(Debug, Eq)