nodejs-semver 7.0.0

A node-semver compliant semver implementation in Rust.
Documentation
use crate::scan::{identifiers, may_start_whitespace, number, skip_whitespace, whitespace_len};
use crate::{Identifiers, Version};

pub(crate) fn parse(input: &str) -> Option<Version> {
    let bytes = input.as_bytes();
    let mut i = usize::from(matches!(bytes.first(), Some(b'v' | b'V')));
    if bytes.get(i).copied().is_some_and(may_start_whitespace) {
        i = skip_whitespace(input, i);
    }

    let (major, next) = number(bytes, i)?;
    i = next;
    if bytes.get(i) != Some(&b'.') {
        return None;
    }
    i += 1;

    let (minor, next) = number(bytes, i)?;
    i = next;
    if bytes.get(i) != Some(&b'.') {
        return None;
    }
    i += 1;

    let (patch, next) = number(bytes, i)?;
    i = next;

    let (pre_release, i) = match bytes.get(i).copied() {
        None => return Some(Version::new_empty(major, minor, patch)),
        Some(b'+') => {
            let (build, _) = identifiers(input, i + 1)?;
            return Some(Version::new_with_identifiers(
                major,
                minor,
                patch,
                Identifiers::Empty,
                build,
            ));
        }
        Some(b'-') => identifiers(input, i + 1)?,
        Some(ch) if ch.is_ascii_alphanumeric() => identifiers(input, i)?,
        Some(ch) if may_start_whitespace(ch) && whitespace_len(input, i) > 0 => {
            return Some(Version::new_empty(major, minor, patch));
        }
        _ => return None,
    };

    let build = if bytes.get(i) == Some(&b'+') {
        identifiers(input, i + 1)?.0
    } else {
        Identifiers::Empty
    };

    Some(Version::new_with_identifiers(
        major,
        minor,
        patch,
        pre_release,
        build,
    ))
}