xmltv 2.1.1

XMLTV for electronic program guide (EPG) parser and generator using serde.
Documentation
//! Root TV document type.
use serde::{Deserialize, Serialize};

use crate::{Channel, Programme};

/// The root element of an XMLTV document.
///
/// Date should be the date when the listings were originally produced in whatever format; if you're
/// converting data from another source, then use the date given by that source. The date when the
/// conversion itself was done is not important.
///
/// To publicize your wonderful program which generated this file, you can use
/// `generator-info-name` (preferably in the form 'progname/version') and `generator-info-url`
/// (a link to more info about the program).
///
/// ```dtd
/// <!ELEMENT tv (channel*, programme*)>
/// <!ATTLIST tv date                CDATA #IMPLIED
///              source-info-url     CDATA #IMPLIED
///              source-info-name    CDATA #IMPLIED
///              source-data-url     CDATA #IMPLIED
///              generator-info-name CDATA #IMPLIED
///              generator-info-url  CDATA #IMPLIED >
/// ```
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Default)]
pub struct Tv {
    /// A URL describing the data source in some human-readable form.
    #[serde(skip_serializing_if = "Option::is_none", rename = "@source-info-url")]
    pub source_info_url: Option<String>,
    /// The link text for `source-info-url`; generally the human-readable name of the listings supplier.
    #[serde(skip_serializing_if = "Option::is_none", rename = "@source-info-name")]
    pub source_info_name: Option<String>,
    /// Where the actual data is grabbed from (machine-readable, if available).
    #[serde(skip_serializing_if = "Option::is_none", rename = "@source-data-url")]
    pub source_data_url: Option<String>,
    #[serde(
        skip_serializing_if = "Option::is_none",
        rename = "@generator-info-name"
    )]
    pub generator_info_name: Option<String>,
    #[serde(
        skip_serializing_if = "Option::is_none",
        rename = "@generator-info-url"
    )]
    pub generator_info_url: Option<String>,
    #[serde(
        skip_serializing_if = "Vec::is_empty",
        default = "Vec::new",
        rename = "channel"
    )]
    pub channels: Vec<Channel>,
    #[serde(
        skip_serializing_if = "Vec::is_empty",
        default = "Vec::new",
        rename = "programme"
    )]
    pub programmes: Vec<Programme>,
}

impl Tv {
    /// Find all programmes belonging to `channel_id`.
    pub fn find_by_channel(&self, channel_id: &str) -> Vec<&Programme> {
        self.programmes
            .iter()
            .filter(|p| p.channel == channel_id)
            .collect()
    }

    /// Return the channel with the given `channel_id`, if it exists.
    pub fn get_channel(&self, channel_id: &str) -> Option<&Channel> {
        self.channels.iter().find(|c| c.id == channel_id)
    }

    /// Sort programmes by start date (lexicographic, works for XMLTV datetime strings).
    pub fn sort_programmes_by_date(&mut self) {
        self.programmes.sort_by(|a, b| a.start.cmp(&b.start));
    }
}