//! Parse TOML frontmatter

use miette::{SourceOffset, SourceSpan};

use crate::{Metadata, parse_simple};

/// TOML parse error
#[derive(Debug, miette::Diagnostic, thiserror::Error)]
#[error("failed to deserialize toml frontmatter")]
pub struct Error {
    /// The 'source' of the frontmatter.
    #[source_code]
    src: String,
    /// The location where [`serde_toml`] failed to parse.
    #[label("{err}")]
    location: Option<SourceSpan>,

    /// The error emitted.
    #[source]
    err: Box<serde_toml::de::Error>,
}

/// Locates start and end `+++` and attempts to parse the contents.
///
/// # Errors
///
/// This will fail if the contents is not valid TOML.
pub fn parse(data: &str) -> Result<(Option<Metadata>, &str), Error> {
    let (frontmatter, contents) = match parse_simple(data, "---") {
        (Some(frontmatter), contents) => (frontmatter, contents),
        (None, contents) => return Ok((None, contents)),
    };

    let parsed = match serde_toml::from_str(frontmatter) {
        Ok(parsed) => parsed,
        Err(err) => {
            return Err(Error {
                src: frontmatter.to_string(),
                location: err.span().map(|span| {
                    SourceSpan::new(SourceOffset::from(span.start), span.end - span.start)
                }),
                err: Box::new(err),
            });
        }
    };

    Ok((Some(parsed), contents))
}
