pub mod jf2;
pub mod parse;
pub use microformats_types as types;

#[derive(thiserror::Error, Debug)]
pub enum Error {
    #[error(transparent)]
    Parse(#[from] parse::Error),

    #[error(transparent)]
    Types(#[from] types::Error),

    #[error(transparent)]
    IO(#[from] std::io::Error),

    #[error(transparent)]
    FromUtf8(#[from] std::string::FromUtf8Error),

    #[error("The required property {name:} was not of the type {kind:?}")]
    InvalidRequiredProperty { name: String, kind: String },

    #[error(transparent)]
    Json(#[from] serde_json::Error),

    #[error(transparent)]
    Jf2Profile(#[from] jf2::profiles::Error),
}

impl PartialEq for Error {
    fn eq(&self, other: &Self) -> bool {
        self.to_string().eq(&other.to_string())
    }
}

impl From<url::ParseError> for Error {
    fn from(value: url::ParseError) -> Self {
        Self::Parse(parse::Error::from(value))
    }
}

impl From<microformats_types::temporal::Error> for Error {
    fn from(value: microformats_types::temporal::Error) -> Self {
        Self::Types(types::Error::from(value))
    }
}

/// Parses the provided HTML into a `types::Document` resolved with the proviedd URL.
///
/// ```
/// use microformats::from_html;
///
/// let document = from_html(r#"
/// <html>
///     <head>
///         <link rel="author me" href="/author">
///     </head>
///     <body>
///     </body>
/// </html>
/// "#, "https://example.com".parse().unwrap());
///
/// assert!(document.is_ok());
/// ```
pub fn from_html(html: &str, url: &url::Url) -> Result<types::Document, Error> {
    parse::Parser::from_html(html.to_string())
        .and_then(|parser| parser.into_document(Some(url.clone())))
}

/// Parses the HTML stored in the provided reader into a `types::Document` resolved with the provided URL.
pub fn from_reader<R>(mut reader: R, url: &url::Url) -> Result<types::Document, Error>
where
    R: std::io::BufRead,
{
    let mut html = String::with_capacity(16384);
    reader
        .read_to_string(&mut html)
        .map_err(Error::IO)
        .and_then(|_| from_html(&html, url))
}

pub mod http;
