//! Parsers for path.

use crate::parser::char;
use crate::parser::str::{find_split2_hole, satisfy_chars_with_pct_encoded};
use crate::spec::Spec;
use crate::validate::{Error, ErrorKind};

/// Returns `Ok(_)` if the string matches `path-abempty` or `ipath-abempty`.
pub(super) fn validate_path_abempty<S: Spec>(i: &str) -> Result<(), Error> {
    if i.is_empty() {
        return Ok(());
    }
    let i = match i.strip_prefix('/') {
        Some(rest) => rest,
        None => return Err(Error::with_kind(ErrorKind::InvalidPath)),
    };
    let is_valid = satisfy_chars_with_pct_encoded(
        i,
        char::is_ascii_pchar_slash,
        S::is_nonascii_char_unreserved,
    );
    if is_valid {
        Ok(())
    } else {
        Err(Error::with_kind(ErrorKind::InvalidPath))
    }
}

/// Returns `Ok(_)` if the string matches `hier-part` or `ihier-part` modulo
/// `"//" authority path-abempty`.
pub(super) fn validate_path_absolute_authority_absent<S: Spec>(i: &str) -> Result<(), Error> {
    if i.is_empty() {
        // `path-empty`.
        return Ok(());
    }
    if i.starts_with("//") {
        unreachable!("this case should be handled by the caller");
    }
    let is_valid = satisfy_chars_with_pct_encoded(
        i,
        char::is_ascii_pchar_slash,
        S::is_nonascii_char_unreserved,
    );
    if is_valid {
        Ok(())
    } else {
        Err(Error::with_kind(ErrorKind::InvalidPath))
    }
}

/// Returns `Ok(_)` if the string matches `relative-part` or `irelative-part` modulo
/// `"//" authority path-abempty`.
pub(super) fn validate_path_relative_authority_absent<S: Spec>(i: &str) -> Result<(), Error> {
    if i.starts_with("//") {
        unreachable!("this case should be handled by the caller");
    }
    let is_valid = match find_split2_hole(i, b'/', b':') {
        Some((_, b'/', _)) | None => satisfy_chars_with_pct_encoded(
            i,
            char::is_ascii_pchar_slash,
            S::is_nonascii_char_unreserved,
        ),
        Some((_, c, _)) => {
            debug_assert_eq!(c, b':');
            // `foo:bar`-style. This does not match `path-noscheme`.
            return Err(Error::with_kind(ErrorKind::InvalidPath));
        }
    };
    if is_valid {
        Ok(())
    } else {
        Err(Error::with_kind(ErrorKind::InvalidPath))
    }
}

/// Returns `Ok(_)` if the string matches `path`/`ipath` rules.
pub(crate) fn validate_path<S: Spec>(i: &str) -> Result<(), Error> {
    let is_valid = satisfy_chars_with_pct_encoded(
        i,
        char::is_ascii_pchar_slash,
        S::is_nonascii_char_unreserved,
    );
    if is_valid {
        Ok(())
    } else {
        Err(Error::with_kind(ErrorKind::InvalidPath))
    }
}

/// Returns `Ok(_)` if the string matches `segment`/`isegment` rules.
pub(crate) fn validate_path_segment<S: Spec>(i: &str) -> Result<(), Error> {
    let is_valid =
        satisfy_chars_with_pct_encoded(i, char::is_ascii_pchar, S::is_nonascii_char_unreserved);
    if is_valid {
        Ok(())
    } else {
        Err(Error::with_kind(ErrorKind::InvalidPath))
    }
}
