link_scraper 0.2.0

Scrape links from any document-file format
Documentation
use crate::formats::xml::xlink::{get_xlink_attribute_value, XLinkFormatError};
use crate::formats::xml::XmlStartElement;

#[derive(Debug)]
enum XLinkType {
    Simple,
    Extended,
    Locator,
    Arc,
    Resource,
    Title,
}

impl TryFrom<&str> for XLinkType {
    type Error = XLinkFormatError;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        match value {
            "simple" => Ok(XLinkType::Simple),
            "extended" => Ok(XLinkType::Extended),
            "locator" => Ok(XLinkType::Locator),
            "arc" => Ok(XLinkType::Arc),
            "resource" => Ok(XLinkType::Resource),
            "title" => Ok(XLinkType::Title),
            _ => Err(XLinkFormatError::UnknownTypeError(value.to_string())),
        }
    }
}

pub enum XlinkElement<'a> {
    Simple(XlinkSimpleElement<'a>),
    Extended(XlinkExtendedElement<'a>),
    Locator(XlinkLocatorElement<'a>),
    Arc(XlinkArcElement<'a>),
    Resource(XlinkResourceElement<'a>),
    Title(XlinkTitleElement<'a>),
}

impl<'a> XlinkElement<'a> {
    pub fn try_from_xml_start_element(
        xml_start_element: XmlStartElement<'a>,
    ) -> Result<Option<Self>, XLinkFormatError> {
        let xlink_href = get_xlink_attribute_value("href", xml_start_element.attributes);
        let mut xlink_type_option = get_xlink_attribute_value("type", xml_start_element.attributes)
            .map(|type_value| XLinkType::try_from(type_value.as_str()))
            .transpose()?;
        if xlink_href.is_some() && xlink_type_option.is_none() {
            xlink_type_option = Some(XLinkType::Simple);
        }
        if xlink_type_option.is_none() {
            return Ok(None);
        }
        match xlink_type_option {
            None => Ok(None),
            Some(xlink_type) => match xlink_type {
                XLinkType::Simple => Ok(Some(XlinkElement::Simple(xml_start_element.try_into()?))),
                XLinkType::Extended => {
                    Ok(Some(XlinkElement::Extended(xml_start_element.try_into()?)))
                }
                XLinkType::Locator => {
                    Ok(Some(XlinkElement::Locator(xml_start_element.try_into()?)))
                }
                XLinkType::Arc => Ok(Some(XlinkElement::Arc(xml_start_element.try_into()?))),
                XLinkType::Resource => {
                    Ok(Some(XlinkElement::Resource(xml_start_element.try_into()?)))
                }
                XLinkType::Title => Ok(Some(XlinkElement::Title(xml_start_element.try_into()?))),
            },
        }
    }
}

pub struct XlinkSimpleElement<'a> {
    pub href: Option<String>,
    pub role: Option<String>,
    pub arcrole: Option<String>,
    pub title: Option<String>,
    pub show: Option<String>,
    pub actuate: Option<String>,
    pub xml: XmlStartElement<'a>,
}
impl<'a> TryFrom<XmlStartElement<'a>> for XlinkSimpleElement<'a> {
    type Error = XLinkFormatError;

    fn try_from(xml_start_element: XmlStartElement<'a>) -> Result<Self, Self::Error> {
        Ok(XlinkSimpleElement {
            href: get_xlink_attribute_value("href", xml_start_element.attributes),
            role: get_xlink_attribute_value("role", xml_start_element.attributes),
            arcrole: get_xlink_attribute_value("arcrole", xml_start_element.attributes),
            title: get_xlink_attribute_value("title", xml_start_element.attributes),
            show: get_xlink_attribute_value("show", xml_start_element.attributes),
            actuate: get_xlink_attribute_value("actuate", xml_start_element.attributes),
            xml: xml_start_element,
        })
    }
}

pub struct XlinkExtendedElement<'a> {
    pub role: Option<String>,
    pub title: Option<String>,
    pub xml: XmlStartElement<'a>,
}
impl<'a> TryFrom<XmlStartElement<'a>> for XlinkExtendedElement<'a> {
    type Error = XLinkFormatError;

    fn try_from(xml_start_element: XmlStartElement<'a>) -> Result<Self, Self::Error> {
        Ok(XlinkExtendedElement {
            role: get_xlink_attribute_value("role", xml_start_element.attributes),
            title: get_xlink_attribute_value("title", xml_start_element.attributes),
            xml: xml_start_element,
        })
    }
}

pub struct XlinkLocatorElement<'a> {
    pub href: String,
    pub role: Option<String>,
    pub title: Option<String>,
    pub xml: XmlStartElement<'a>,
}
impl<'a> TryFrom<XmlStartElement<'a>> for XlinkLocatorElement<'a> {
    type Error = XLinkFormatError;

    fn try_from(xml_start_element: XmlStartElement<'a>) -> Result<Self, Self::Error> {
        Ok(XlinkLocatorElement {
            href: get_xlink_attribute_value("href", xml_start_element.attributes).ok_or(
                XLinkFormatError::MissingRequiredAttributeError("href".to_string()),
            )?,
            role: get_xlink_attribute_value("role", xml_start_element.attributes),
            title: get_xlink_attribute_value("title", xml_start_element.attributes),
            xml: xml_start_element,
        })
    }
}

pub struct XlinkArcElement<'a> {
    pub arcrole: Option<String>,
    pub title: Option<String>,
    pub show: Option<String>,
    pub actuate: Option<String>,
    pub from: Option<String>,
    pub to: Option<String>,
    pub xml: XmlStartElement<'a>,
}
impl<'a> TryFrom<XmlStartElement<'a>> for XlinkArcElement<'a> {
    type Error = XLinkFormatError;

    fn try_from(xml_start_element: XmlStartElement<'a>) -> Result<Self, Self::Error> {
        Ok(XlinkArcElement {
            arcrole: get_xlink_attribute_value("arcrole", xml_start_element.attributes),
            title: get_xlink_attribute_value("title", xml_start_element.attributes),
            show: get_xlink_attribute_value("show", xml_start_element.attributes),
            actuate: get_xlink_attribute_value("actuate", xml_start_element.attributes),
            from: get_xlink_attribute_value("from", xml_start_element.attributes),
            to: get_xlink_attribute_value("to", xml_start_element.attributes),
            xml: xml_start_element,
        })
    }
}

pub struct XlinkResourceElement<'a> {
    pub role: Option<String>,
    pub title: Option<String>,
    pub label: Option<String>,
    pub xml: XmlStartElement<'a>,
}
impl<'a> TryFrom<XmlStartElement<'a>> for XlinkResourceElement<'a> {
    type Error = XLinkFormatError;

    fn try_from(xml_start_element: XmlStartElement<'a>) -> Result<Self, Self::Error> {
        Ok(XlinkResourceElement {
            role: get_xlink_attribute_value("role", xml_start_element.attributes),
            title: get_xlink_attribute_value("title", xml_start_element.attributes),
            label: get_xlink_attribute_value("label", xml_start_element.attributes),
            xml: xml_start_element,
        })
    }
}

pub struct XlinkTitleElement<'a> {
    pub xml: XmlStartElement<'a>,
}
impl<'a> TryFrom<XmlStartElement<'a>> for XlinkTitleElement<'a> {
    type Error = XLinkFormatError;

    fn try_from(_xml_start_element: XmlStartElement<'a>) -> Result<Self, Self::Error> {
        Ok(XlinkTitleElement {
            xml: _xml_start_element,
        })
    }
}