//! # kvf — Kv Format 1.0 Parser
//!
//! `kvf` is a minimal, spec-compliant parser for [Kv Format 1.0](https://kvformat.org/).
//!
//! ## Quick start
//!
//! ```rust
//! use kvf::{parse, Entry};
//!
//! let input = b"APP_NAME=My Application\nDEBUG=true\n";
//! let entries = parse(input).unwrap();
//! for entry in &entries {
//!     if let Entry::Kv { key, value, .. } = entry {
//!         println!("{key} = {value}");
//!     }
//! }
//! ```
//!
//! ## Parser configuration
//!
//! ```rust
//! use kvf::{parse_with_config, Entry, ParserConfig};
//!
//! let config = ParserConfig {
//!     emit_comments: false,
//!     emit_blanks: false,
//!     emit_shebang: false,
//! };
//! let input = b"# comment\nKEY=value\n";
//! let entries = parse_with_config(input, &config).unwrap();
//! assert_eq!(entries.len(), 1);
//! ```
//!
//! ## Streaming
//!
//! ```rust
//! use kvf::parse_iter;
//!
//! let input = b"KEY=value\n";
//! for result in parse_iter(input) {
//!     println!("{:?}", result);
//! }
//! ```

#![forbid(unsafe_code)]

mod entry;
mod error;
mod parser;

pub use entry::{Entry, ParserConfig};
pub use error::ParseError;

#[cfg(test)]
mod tests;

// ---------------------------------------------------------------------------
// Eager API
// ---------------------------------------------------------------------------

/// Parse the entire input at once.
///
/// Returns `Ok(entries)` when the input is valid, or `Err(errors)` containing
/// all detected errors when the input is invalid. On error, no entries are
/// returned.
///
/// Uses the default [`ParserConfig`] (comments emitted, blanks and shebang
/// suppressed).
///
/// # Example
///
/// ```rust
/// use kvf::{parse, Entry};
///
/// let entries = parse(b"KEY=value\n").unwrap();
/// assert_eq!(entries[0], Entry::Kv { line: 1, key: "KEY".into(), value: "value".into() });
/// ```
pub fn parse(input: &[u8]) -> Result<Vec<Entry>, Vec<ParseError>> {
    parse_with_config(input, &ParserConfig::default())
}

/// Parse the entire input at once with a custom [`ParserConfig`].
///
/// Returns `Ok(entries)` when the input is valid, or `Err(errors)` on failure.
pub fn parse_with_config(
    input: &[u8],
    config: &ParserConfig,
) -> Result<Vec<Entry>, Vec<ParseError>> {
    let mut entries = Vec::new();
    let mut errors = Vec::new();

    for item in parser::make_iter(input, config) {
        match item {
            Ok(entry) => {
                // Apply config-level filtering.
                let keep = match &entry {
                    Entry::Comment { .. } => config.emit_comments,
                    Entry::Blank { .. } => config.emit_blanks,
                    Entry::Shebang { .. } => config.emit_shebang,
                    Entry::Kv { .. } => true,
                };
                if keep {
                    entries.push(entry);
                }
            }
            Err(e) => errors.push(e),
        }
    }

    if errors.is_empty() {
        Ok(entries)
    } else {
        Err(errors)
    }
}

// ---------------------------------------------------------------------------
// Streaming API
// ---------------------------------------------------------------------------

/// Return a streaming iterator over the input.
///
/// Each call to [`Iterator::next`] yields a `Result<Entry, ParseError>` for
/// the next line. Entries that are suppressed by the default [`ParserConfig`]
/// (blanks and shebang) are filtered out automatically.
///
/// Uses the default [`ParserConfig`].
pub fn parse_iter(input: &[u8]) -> impl Iterator<Item = Result<Entry, ParseError>> + '_ {
    parse_iter_with_config(input, &ParserConfig::default())
}

/// Return a streaming iterator over the input with a custom [`ParserConfig`].
pub fn parse_iter_with_config<'a>(
    input: &'a [u8],
    config: &ParserConfig,
) -> impl Iterator<Item = Result<Entry, ParseError>> + 'a {
    let emit_comments = config.emit_comments;
    let emit_blanks = config.emit_blanks;
    let emit_shebang = config.emit_shebang;

    parser::make_iter(input, config).filter(move |item| match item {
        Ok(Entry::Comment { .. }) => emit_comments,
        Ok(Entry::Blank { .. }) => emit_blanks,
        Ok(Entry::Shebang { .. }) => emit_shebang,
        _ => true,
    })
}
